Viele
Viele

Reputation: 2336

Typescript: how to explain this interaction between extends and function type

Example 1 makes perfect sense to me

type X = (1 | 2 | 3) extends (infer I) ? [I] : never;

// X = [1 | 2 | 3]

Example 2 I don't know why the type variables are now intersected

type X = (
    ((_: 1) => void) | ((_: 2) => void) | ((_: 3) => void)
) extends ((_: infer I) => void) ? [I] : never;

// X = [1 & 2 & 3]

My guess is that has some relationship/similarity to this:

type X = { x: number } | { y: number } | { z: number };
type Y = keyof X;

// Y = x & y & z

However, I am not able to convince myself I understood it from first principle. Would love to hear how this can be explained.

Upvotes: 6

Views: 386

Answers (1)

artem
artem

Reputation: 51669

It was expained in the release notes when type inference from conditional types was introduced. Whether it will be a union or intersection depends on the variance of the inferred type. Intuitively, if a type is inferred as a common type for several values, it can be any of them (union), but if it's inferred as argument type for several functions, it must be acceptable by any of them (intersection).

Quote from the release notes:

The following example demonstrates how multiple candidates for the same type variable in co-variant positions causes a union type to be inferred:

type Foo<T> = T extends { a: infer U, b: infer U } ? U : never;
type T10 = Foo<{ a: string, b: string }>;  // string
type T11 = Foo<{ a: string, b: number }>;  // string | number

Likewise, multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred:

type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>;  // string
type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>;  // string & number

Further discussion can be found in the PR implementing this feature.

As a side note, it enables some cool tricks like union to intersection type conversion.

Upvotes: 5

Related Questions