When comparing a generic type to a conditional type whose checkType is the same type, we have additional information that we are not utilizing.. for instance:
function f<T extends number>(x: T) {
var y: T extends number ? number : string;
// `T` is not assignable to `T extends number ? number : string`
y = x;
}
Ignoring intersections, we should be able to take the true branch all the time based on the constraint.
The intuition here is that T in the example above is really T extends number ? T : never which is assignable to T extends number ? number : string.
Similarly, with substitution types, we have additional information that we can leverage, e.g.:
declare function isNumber(a: any): a is number;
function map<T extends number | string>(
o: T,
map: (value: T extends number ? number : string) => any
): any {
if (isNumber(o)) {
// `T & number` is not assignable to `T extends number ? number : string`
return map(o);
}
}
When comparing a generic type to a conditional type whose checkType is the same type, we have additional information that we are not utilizing.. for instance:
Ignoring intersections, we should be able to take the true branch all the time based on the constraint.
The intuition here is that
Tin the example above is reallyT extends number ? T : neverwhich is assignable toT extends number ? number : string.Similarly, with substitution types, we have additional information that we can leverage, e.g.: