我一直在研究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 来推断类型。
我没有具体了解的是两个示例中括号和方括号的作用以及作用。我将不胜感激。如果您需要更多地解释如何推断或扩展工作,请放心,但是就像我说的那样,我认为我得到了那些概念……也许没有。真正令人困惑的一件事是,如果我拿掉() /括号,那一切就搞砸了。为什么呢?
我在以下假设中正确吗?
type test2 = string | number
进入
type test2 = ( string | number )
注意括号。
T extends (infer U)[]
谢谢:)
答案 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"
事实证明,keyof
与A
的绑定比联合运算符|
的绑定更紧密,因此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]
。因此T
是U
的{{1}}属性的类型,即{a: string}["a"]
。而a
是您使用{a: string}
键索引到string
时得到的类型,即Array<boolean>[number]
。
当方括号包含用逗号分隔的零个或多个类型列表时,例如Array<boolean>
或number
或boolean
或[]
等,(和如果它是零或一,则需要注意不要将其与上面的数组或查找符号混淆),那么您指定的是tuple类型,即在固定的数组中具有固定数量的可能不同类型的元素的数组订购。元组类型也可以以[T]
开头来构成readonly tuples,并且元组还有其他有趣的事情,例如rest and optional elements in tuple types和variadic 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[]
推断arr
为Array<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[])[]
。
我希望能为您澄清一些事情。祝你好运!