Typescript:如何解释扩展和函数类型之间的交互

Vie*_*ele 6 types type-systems typescript

示例 1 对我来说非常有意义

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

// X = [1 | 2 | 3]
Run Code Online (Sandbox Code Playgroud)

示例 2 我不知道为什么类型变量现在相交

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

// X = [1 & 2 & 3]
Run Code Online (Sandbox Code Playgroud)

我的猜测是,这与此有一些关系/相似之处:

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

// Y = x & y & z
Run Code Online (Sandbox Code Playgroud)

然而,我无法说服自己我从第一原理就理解了它。很想听听如何解释这一点。

art*_*tem 5

当引入条件类型的类型推断时,发行说明中对此进行了解释。它是并集还是交集取决于推断类型的方差。直观上,如果一个类型被推断为多个值的公共类型,则它可以是其中任何一个(并集),但如果它被推断为多个函数的参数类型,则它必须被它们中的任何一个所接受(交集)。

引用发行说明:

以下示例演示了协变位置中同一类型变量的多个候选者如何导致推断出联合类型:

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
Run Code Online (Sandbox Code Playgroud)

同样,逆变位置中同一类型变量的多个候选会导致推断交集类型:

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
Run Code Online (Sandbox Code Playgroud)

进一步的讨论可以在实现此功能的 PR中找到。

附带说明一下,它可以实现一些很酷的技巧,例如并集到交集类型转换