Reputation: 5625
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?
Upvotes: 8
Views: 81
Reputation: 328362
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.
Upvotes: 2