I am trying to gain a better understanding of the extends keyword in TypeScript and its potential applications.
One thing I have come across are two built-in utilities, Extract and Exclude that leverage both extends and Conditional Typing.
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
I was playing around to better understand how this "narrowing down", or to better say "subset filtering" works, and have tried creating my own implementation just to see it in action, and have come across this really odd behaviour:
Link to the Playground Example:
type ValueSet = string | 'lol' | 0 | {a: 1} | number[] | 643;
type CustomExclude<T, U> = T extends U ? T : never;
// this works:
// type Result1 = 0 | 643
type Result1 = CustomExclude<ValueSet, number>;
// but this doesn't?
// type Result2 = never
type Result2 = ValueSet extends number ? ValueSet : never;
Why does that happen?
I would expect both instances to return the correct subset of the type, but the conditional typing only works if express through Generics.
Can someone explain me the logic behind this?