我对这种类型的工作原理相当困惑。它接受一个数组并扩展到一个函数?它不应该扩展到一个数组吗?因为它处理的是一个数组?我希望有人能解释一下为什么它可以扩展到一个函数,什么是U?
type Prepend<E, T extends any[]> =
((head: E, ...args: T) => any) extends ((...args: infer U) => any)
? U
: T
第二种类型的行为也类似。它接受一个数组,但将其作为函数处理。
type Tail<T extends readonly any[]> =
((...t:T) => any) extends ((_:any, ...tail: infer TT) => any)
? TT
: []
示例:
type T1 = Tail<[1,2,3]> //2 ,3
type T2 = Tail<["A","B","C"]> // "B" , "C"
type T3 = Tail<[1,"A","Hello"]> //"A", "Hello"
type P1 = Prepend<string, []> //[string]
type P2 = Prepend<number, [1,2]> //[number,1,2]
1条答案
按热度按时间juzqafwq1#
在引入可变元组类型之前,没有直接的方法来连接或拆分元组类型。今天你会写
Prepend
类型现在相当无用,因为它比直接使用可变元组要长。但是对于Tail
,如果你理解了infer
的条件类型推断,你仍然可以看到发生了什么。编译器正在将T
类型与某个元组进行匹配,该元组的第一个元素是any
(它几乎可以匹配任何东西),而元组的 rest 是某个infer
红色类型TT
,它的计算结果为TT
。但在此之前,唯一可用的方法是在元组类型和函数参数之间进行转换。像
[string, number]
这样的元组和像(a: string, b: number) => any
这样的函数之间有一个自然的Map,编译器支持它们之间的各种转换,如TypeScript 3.0发行说明中所述。注意函数参数的名称(以及后面出现的标记元组的 labels)不会影响类型,因此在下面的
(a: string) => any
和(x: string) => any
是相同的。而(...args: [string]) => any
和(...rest: [string]) => any
和(...blfdsjksdksjk: [string]) => any
是相同的。无论如何,
(a: string, b: number) => any
类型的函数有一个[string, number]
类型的参数列表。由于rest parameters允许将参数列表打包到单个参数位置,因此可以说(a: string, b: number) => any
和(...args: [string, number]) => any
是同一类型。现在函数形参列表和实参之间的关系应该更加明显了。事实上,所有这些都是同一类型的:
看看
F3
,希望这是有意义的:很容易匹配第一个参数(因为
any
匹配任何东西),然后rest参数的类型是[2, 3, 4]
。但由于F3
与F2
相同,因此您得到就像这样,我们已经得到了
[1, 2, 3, 4]
元组的尾部,通过从元组类型转换为函数类型,操作函数类型,然后转换回元组。这种技术可以成为一种实用程序类型:这就是你要问的
与
Prepend
类似,您可以在另一个方向进行匹配,其中您获取(head: E, ...args: T) => any
类型的函数并尝试根据(...args: infer U) => any
推断它,这将导致U
与我们现在称为[E, ...T]
的相同。Playground链接到代码