在TypeScript的类型系统中,数组类型分为普通数组和元组两种形式,普通数组的元素类型统一,而元组则允许每个索引位置对应不同的类型。当我们需要从已有的数组类型中推断元素类型,并且要保留每个索引对应的类型信息时,直接使用基础的类型推断方式往往无法满足需求。

基础数组类型推断的局限性
如果我们只是简单使用类型推断获取数组的元素类型,通常会丢失索引相关的信息。比如对于元组类型[string, number],普通的元素类型提取只会得到string | number,无法区分索引0对应string,索引1对应number。
先看一个基础的类型推断示例:
// 普通的元素类型提取,会丢失索引信息 type GetElementType<T extends any[]> = T[number]; type Tuple = [string, number]; type ElementType = GetElementType<Tuple>; // 结果为 string | number,无法区分索引
通过映射类型保留索引信息
要保留数组的索引信息,核心思路是利用TypeScript的映射类型,遍历数组的索引而不是元素,重新构造一个和原数组索引对应的新类型。因为数组的索引本质是数字字面量类型,遍历这些索引就可以保留每个位置的类型对应关系。
具体实现如下:
// 保留索引信息的数组类型推断
type InferArrayWithIndex<T extends any[]> = {
[K in keyof T]: T[K];
};
type Tuple = [string, number, boolean];
type Result = InferArrayWithIndex<Tuple>;
// Result 依然是 [string, number, boolean],索引信息完全保留
这里的keyof T对于数组类型来说,会返回数组的索引类型以及数组自带的属性类型,但因为我们用的是K in keyof T遍历,并且只取T[K],最终生成的新类型和原数组类型的索引对应关系完全一致。
结合infer关键字提取元组索引信息
如果我们需要从更复杂的数组类型中提取信息,比如已知一个函数参数是一个元组,要推断这个元组的索引和类型,可以结合infer关键字来实现。比如提取函数参数元组的每个索引类型:
// 提取函数参数元组的索引类型
type GetArgsWithIndex<T> = T extends (...args: infer Args) => any ? Args : never;
function testFunc(a: string, b: number, c: boolean): void {}
type ArgsType = GetArgsWithIndex<typeof testFunc>;
// ArgsType 为 [string, number, boolean],索引信息保留
实际场景应用示例
在实际开发中,比如我们要实现一个函数,接收元组类型的参数,并且返回一个新的元组,新元组的每个索引位置是原元组对应位置元素的字符串形式,这时候就需要保留原数组的索引信息:
// 将元组每个元素转为字符串,保留索引
type MapTupleToString<T extends any[]> = {
[K in keyof T]: string;
};
type OriginTuple = [string, number, boolean];
type StringTuple = MapTupleToString<OriginTuple>;
// StringTuple 为 [string, string, string],索引数量和原元组一致
// 对应的函数实现
function mapTupleToString<T extends any[]>(tuple: T): MapTupleToString<T> {
return tuple.map(item => String(item)) as any;
}
const res = mapTupleToString(['a', 1, true]);
// res 的类型是 [string, string, string],索引信息保留
注意事项
- 普通数组(元素类型统一)使用上述方式推断后,依然会保持普通数组的类型,不会自动转为元组,只有原类型是元组时才会保留元组的索引特性。
- 如果数组类型中包含可选元素,比如
[string?, number?],映射后可选索引的特性也会保留。 - 不要混淆
T[number]和索引遍历的区别,前者获取的是所有元素的联合类型,后者保留每个索引的对应关系。
TypeScript数组类型推断索引信息保留泛型infer修改时间:2026-07-17 10:45:27