Why does TypeScript not apply type narrowing on members of objects to the object type itself, such that it can be passed to another function that expects a narrowed type? How can this be fixed/circumvented without loosing typesafety?
Minimal example:
type A = { key: string | null};
type B = {key: string};
function func(a: B) {};
const a: A = {key:'abcd'};
if(typeof(a.key)==='string') {
a.key // has (narrowed) type 'string'
func(a); // still does not work
}
The error message is: Types of property 'key' are incompatible. Type 'string | null' is not assignable to type 'string'.
This is ultimately a design limitation of TypeScript. It does not use type guards on an object's properties to narrow the object itself, except in the particular case where the object is a discriminated union type... and in your case, A
isn't a union at all, let alone a discriminated one.
The way I'd do this for your case is to introduce a user-defined type guard function which explicitly performs the narrowing you expect: you pass in an object with a key
property, and return true
or false
depending on whether or not this object is a valid B
:
const isB = (x: { key: any }): x is B => typeof x.key === "string";
Then you use it:
if (isB(a)) {
func(a); // okay
}
This is (give or take a function call) essentially the same logic as your code, but the compiler recognizes your intent now. Okay, hope that helps; good luck!