Typescript has the ability to infer the type of a value based on queries made within if statements. For instance, the type of one member of an object can be deduced based on another:
type ChildType = 'a' | 'b';
type Child<T extends ChildType> = T extends 'a' ? { type: 'a', color: 'blue' } : T extends 'b' ? { type: 'b', color: 'red' } : never;
interface Parent<T extends Child<ChildType>> {
child: T extends Child<infer R> ? Child<R> : never;
}
function test<T extends Parent<Child<any>>>(parent: T) {
if (parent.child.type === 'a') {
parent.child.color === 'red'; // raises error as it should be blue
}
}
However, when trying to achieve the same functionality by checking nested members of a type, the behavior seems different.
type ChildType = 'a' | 'b';
interface BaseParent<T extends Child<ChildType>> {
child: T;
}
interface Child<T extends ChildType> {
type: T;
}
type Parent<T extends ChildType>
= T extends 'a' ? { color: 'blue' } & BaseParent<Child<'a'>>
: T extends 'b' ? { color: 'red', opacity: 2 } & BaseParent<Child<'b'>>
: never;
function test<T extends Parent<ChildType>>(parent: T) {
if (parent.child.type === 'a') {
parent.color === 'red'; // no error thrown even though there should be
}
}
This issue can be resolved using type guards, however I am exploring alternative methods that do not require them.