Please consider using this helper:
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
in your case. in
operator works as expected mostly with unions. Please see here, here and here
Working solution:
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
const foo = (obj: unknown) => {
if (typeof obj === 'object' && obj) {
if (hasProperty(obj, 'foo') && typeof obj.foo === 'string') {
return obj.foo;
}
}
};
Playground
However, since you want to throw an error if obj
is invalid, you can use assert function
:
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
function foo(obj: unknown): asserts obj is { foo: string } {
const isValid =
typeof obj === 'object' &&
obj &&
hasProperty(obj, 'foo') &&
typeof obj.foo === 'string';
if (!isValid) {
throw new Error();
}
};
declare var obj: unknown;
foo(obj);
obj.foo // ok
Playground