Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent behaviour when impossible intersections produce `never` type

Tags:

typescript

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?

like image 209
Joji Avatar asked Jul 02 '26 16:07

Joji


1 Answers

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.

like image 138
jcalz Avatar answered Jul 05 '26 05:07

jcalz