I found that TypeScript produces never type at different levels when impossible intersections happen
type Foo = {name: string, age: string}
type Bar = {name: string, age: number}
type Baz = Foo & Bar
const baz: Baz = {name: '123,', age:13} // ❌ Type 'number' is not assignable to type 'never'.
The above example results in type Baz to be {name: string, age:never}
However if we slightly change Bar
type Foo = {name: string, age: string}
type Bar = {name: string, age: boolean}
type Baz = Foo & Bar // the whole `Baz` type is `never`
Now the whole Baz type is never. I cannot figure out as to why TypeScript did this. Can someone help me understand this?
Before TypeScript 3.9, both intersections would behave like your first example and be equivalent to {name: string, age: never}. TypeScript 3.9 introduced functionality to reduce intersections by discriminant properties. Discriminant properties are, roughly, those whose values are of single literal types (like string literal types, numeric literal types, boolean literal types, null, and undefined) or unions of such types. These are used in discriminated unions. If an intersection type causes a discriminant property to reduce to never, the compiler will reduce the whole intersection to never. See microsoft/TypeScript#36696 for details and motivation.
Anyway, the type boolean is represented as the union true | false of boolean literals, and therefore the age property in your second example is considered to be a discriminant property. On the other hand, number is a non-literal type and so the age property in your first example is not considered to be a discriminant property. This accounts for the difference in behavior you are seeing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With