TypeScript中Prepend和Tail类型疑问:为何用函数扩展处理数组?
解析TypeScript的Prepend和Tail类型实现逻辑
为什么用函数类型扩展处理数组?
这种写法是TypeScript早期版本的实用技巧——当时直接对数组类型做剩余元素推断的语法支持还不完善,开发者便借助函数参数的剩余参数推断能力,实现数组的前置元素拼接和尾部元素截取。虽然现在TypeScript已经支持更直观的数组扩展写法,但这类旧写法依然是类型编程技巧的典型示例,能帮你理解TypeScript类型系统的灵活特性。
函数参数的infer推断对剩余参数的处理非常精准:当把数组类型作为函数的剩余参数时,TypeScript可以完整捕获参数列表的结构,进而映射为对应的数组类型。
Prepend类型中的U是什么?
U是通过infer关键字推断出来的完整函数参数数组类型。具体逻辑如下:
- 先构造一个函数类型:第一个参数是要前置的元素
E,剩余参数是输入数组T,即(head: E, ...args: T) => any。 - 用条件类型将这个函数与
(...args: infer U) => any匹配——infer U会自动捕获整个函数的参数列表,也就是[E, ...T]。 - 最后返回
U,就得到了把E前置到数组T后的新数组类型。
逐行拆解两个类型
Prepend类型
type Prepend<E, T extends any[]> = ((head: E, ...args: T) => any) extends ((...args: infer U) => any) ? U : T
- 输入:待前置元素
E、目标数组T - 核心逻辑:通过函数参数的结构映射,将
E与T拼接成新数组 - 示例验证:
Prepend<number, [1,2]>生成函数(head: number, ...args: [1,2]) => any,推断出U = [number,1,2],最终返回该数组。
Tail类型
type Tail<T extends readonly any[]> = ((...t:T) => any) extends ((_:any, ...tail: infer TT) => any) ? TT : []
- 输入:只读数组
T - 核心逻辑:通过忽略函数第一个参数、推断剩余参数的方式,提取数组去掉首元素后的部分
- 示例验证:
Tail<[1,2,3]>生成函数(...t: [1,2,3]) => any,匹配时TT会被推断为[2,3],最终返回该数组。
现代简化写法
现在TypeScript支持直接对数组做infer推断,这两个类型可以简化为更易读的形式:
// 简化版Prepend type Prepend<E, T extends any[]> = [E, ...T]; // 简化版Tail type Tail<T extends readonly any[]> = T extends [any, ...infer TT] ? TT : [];
效果和原写法完全一致,但可读性大幅提升。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

