Joji
Joji

Reputation: 5625

Inconsistent behaviour when impossible intersections produce `never` type

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

Answers (1)

jcalz
jcalz

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

Related Questions