打字稿:如何解释扩展和函数类型之间的这种相互作用

时间:2019-03-01 00:55:42

标签: typescript types type-systems

示例1对我来说很有意义

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

// X = [1 | 2 | 3]

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

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

// X = [1 & 2 & 3]

我的猜测是与此相关或相似:

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

// Y = x & y & z

但是,我无法说服自己,我从第一条原则中就理解了这一点。很想听听如何解释这一点。

1 个答案:

答案 0 :(得分:3)

在引入type inference from conditional types的发行说明中对此进行了解释。它将是联合还是相交取决于推断类型的variance。直观地,如果一个类型被推断为多个值的通用类型,则可以是它们中的任何一个(联合),但是如果它被推断为多个函数的参数类型,则它们中的任何一个都必须可以接受(交集)。

从发行说明中引用:

  

以下示例说明了如何将多个候选   协变量位置中的相同类型变量导致联合类型为   推断:

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
  

同理,在反变量位置,同一类型变量的多个候选会导致相交类型的推断:

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

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

作为旁注,它启用了一些很酷的技巧,例如union to intersection type conversion