如何实现适用于数组/元组的TypeScript Pick等效工具类型?
实现适用于数组/元组的Pick工具类型
TypeScript内置的Pick<T, K>是为对象设计的,直接作用于元组时会返回带数字索引的对象类型,而非预期的元组结构。下面提供两种实现方案,满足不同场景需求:
基础方案:生成联合类型数组
适合只需要元素类型集合,不严格要求元组顺序的场景:
// 提取元组的所有数字索引(排除数组原型属性) type TupleIndices<T extends readonly any[]> = Exclude<keyof T, keyof any[]>; // 筛选出目标索引集合中的元组索引 type FilteredIndices<T extends readonly any[], K extends number> = TupleIndices<T> extends infer I ? I extends K ? I : never : never; // 生成包含指定索引元素的数组类型 type TuplePick<T extends readonly any[], K extends number> = { [P in FilteredIndices<T, K>]: T[P] } extends { [key: number]: infer V } ? V[] : never;
使用示例
type Foo = [a: 'a', b: 'b', c: 'c']; type Bar = TuplePick<Foo, 1 | 2>; // 类型为 ('b' | 'c')[] const bar: Bar = ['b', 'c']; // 合法 const invalidBar: Bar = ['a', 'b']; // 类型错误
进阶方案:生成严格顺序的元组类型
如果需要保留元组的原始顺序、元素标签和精确字面量类型,使用递归遍历实现:
type TuplePick< T extends readonly any[], K extends number, _CurrentIndex extends any[] = [], // 追踪当前遍历的索引位置 _Result extends any[] = [] // 收集符合条件的元素 > = T extends [infer First, ...infer Rest] ? _CurrentIndex['length'] extends K // 当前索引命中目标,加入结果集后继续遍历 ? TuplePick<Rest, K, [..._CurrentIndex, unknown], [..._Result, First]> // 当前索引未命中,继续遍历剩余元素 : TuplePick<Rest, K, [..._CurrentIndex, unknown], _Result> : _Result;
使用示例
type Foo = [a: 'a', b: 'b', c: 'c']; type Bar = TuplePick<Foo, 1 | 2>; // 精确类型:[b: 'b', c: 'c'] const bar: Bar = ['b', 'c']; // 完全合法 const wrongBar: Bar = ['c', 'b']; // 类型错误,顺序不匹配
核心逻辑说明
- 基础方案:通过提取元组的数字索引,筛选目标索引后映射为元素类型,最终转换为数组类型。
- 进阶方案:递归遍历元组的每个元素,通过追踪当前索引位置,收集符合目标索引的元素,生成严格匹配顺序的元组类型。
内容的提问来源于stack exchange,提问作者TrevTheDev
相关产品推荐
相关产品推荐

