高级条件类型-推断语法

时间:2020-07-27 01:51:03

标签: typescript typescript-generics

简介

我一直在研究TS的一些高级功能,并发现了一些可以做的很棒的事情。吸引我眼球的一件很酷的事情是提取数组中每个元素的所有类型。但是,在这个特定示例中的语法令人困惑。

我在此注释的正下方突出显示了这两个示例中最令人困惑的部分,在两个示例中都略有不同。

问题专线

T extends (infer U)[]

示例1

type ArrayTypes<T> = 
    T extends (infer U)[]
    ? U
    : never

let arr = [1, "2", []];
type test = ArrayTypes<typeof arr> // type test = string | number | any[]

示例2

type ArrayTypes<T> = 
    T extends (infer U)
    ? U
    : never

let arr = [1, "2", []];
type test = ArrayTypes<typeof arr> // type test = (string | number | any[])[]

问题

现在,我了解了泛型(大部分),所以我了解了 T 的含义,也了解了扩展的作用(大部分)。我也了解,只要可以使用通用类型 ,就可以使用 U 推断类型。

我没有具体了解的是两个示例中括号和方括号的作用以及作用。我将不胜感激。如果您需要更多地解释如何推断或扩展工作,请放心,但是就像我说的那样,我认为我得到了那些概念……也许没有。真正令人困惑的一件事是,如果我拿掉() /括号,那一切就搞砸了。为什么呢?

我在以下假设中正确吗?

  1. 在后台,在类型空间中,括号是定义联合类型的更严格方法?因此打字稿将打开以下内容
type test2 = string | number

进入


type test2 = ( string | number )

注意括号。

  1. (推断U)的意思是-定义一个新的联合类型,但是推断它并将其分配给U。它也构成了数组的类型吗?
T extends (infer U)[] 

谢谢:)

1 个答案:

答案 0 :(得分:2)

类型中的

括号仅用于分组和解决歧义;它们不会修改其周围的类型。例如,给定类型

type A = { a: 1, c: 3 };
type B = { b: 2, c: 3 };

那么这种类型能解决什么?

type Z = keyof A | B; // ??

使用括号,您可以清楚地表示要解释的含义:

type X = (keyof A) | B; // "a" | "c" | B
type Y = keyof (A | B); // "c"

事实证明,keyofA的绑定比联合运算符|的绑定更紧密,因此Z等效于X ...因此如果您是说Y,则需要这些括号。

(越来越过时的)TypeScript Specification document说:

将联合,交集,函数或构造函数类型用作数组元素类型时,必须使用括号;交集类型中的联合,函数或构造函数类型周围;以及联合类型中的函数或构造函数类型。

如有疑问,请添加括号。


类型中的

方括号实际上具有多种不同的含义:

  • 当类型T后紧跟一对 empty 方括号时,表示元素类型为{{的 array 1}},等效于T。因此Array<T>是一个数组,其元素的类型为{a: string}[],等效于{a: string}。 (类似地,如果这样的类型紧跟Array<{a: string}>之类的readonly修饰符,则它等效于readonly T[]。)

  • 当类型ReadonlyArray<T>之后紧跟方括号括起来的另一种类型T(即U)时,方括号不为空,这表示您是looking up or indexing的属性类型,其键的类型为T[U]。因此TU的{​​{1}}属性的类型,即{a: string}["a"]。而a是您使用{a: string}键索引到string时得到的类型,即Array<boolean>[number]

  • 当方括号包含用逗号分隔的零个或多个类型列表时,例如Array<boolean>numberboolean[]等,(和如果它是零或一,则需要注意不要将其与上面的数组或查找符号混淆),那么您指定的是tuple类型,即在固定的数组中具有固定数量的可能不同类型的元素的数组订购。元组类型也可以以[T]开头来构成readonly tuples,并且元组还有其他有趣的事情,例如rest and optional elements in tuple typesvariadic tuple types,因此[T,U]是有效的TS类型(在TS4.0 +中)。


为此,让我们看一下这些示例:

[T,U,V]

这是说:给定类型readonly,如果它扩展了某个数组类型readonly [0, ...[1, 2, 3], 4?, ...[5?, 6?, 7?], ...8[]],其中要推断type ArrayTypes<T> = T extends (infer U)[] ? U : never ,则返回T,否则返回{{ 1}}。 U[]需要附加到U。无论出于何种原因,U的绑定比never绑定得更紧密,因此infer会被解释为U ...这是无效的,因为您只能[]一个新类型的变量,而不是该变量的函数。这就是为什么需要括号。

infer

这很有意义,对吧? T extends infer U[]的类型为T extends infer (U[])或等效的infer,因此let arr = [1, "2", []]; type test = ArrayTypes<typeof arr> // type test = string | number | never[] 推断arrArray<string | number | Array<never>>,这就是您得到的。 (请注意,在我正在使用的TS版本中,值(string | number | never[])[]被推断为类型ArrayTypes<typeof arr>,而不是U。)

此外,请注意,上面带有string | number | never[]键的查找类型不需要[]即可完成相同的操作:

never[]

下一个示例:

any[]

这里没有括号,因为没有歧义。您可以写number。这意味着:给定类型infer扩展了一些推断的类型type ArrayTypes<T> = T extends any[] ? T[number]: never; ,则返回type ArrayTypes<T> = T extends (infer U) ? U : never ;否则返回T extends infer U ? U : never。但这似乎有点愚蠢,因为无论如何T总是被推断为U,因此U总是never

U

类型T将与ArrayTypes<T>相同,即T,或者等效地为let arr = [1, "2", []]; type test = ArrayTypes<typeof arr> // type test = (string | number | never[])[]


我希望能为您澄清一些事情。祝你好运!

Playground link to code