为数组元素添加枚举属性的TypeScript函数类型定义问题
为枚举函数添加正确的TypeScript类型定义
先看原JavaScript实现:
function enumerate( array, offset = 0, enumerationKey = 'position' ) { return array.map((item, index) => ({ ...item, [enumerationKey]: offset + index, })); }
这个函数的作用是给数组每个元素添加一个枚举属性,默认键为'position',支持自定义键名。下面分析你两次尝试的问题,并给出正确的TypeScript类型实现。
两次尝试的问题分析
第一次尝试
function enumerate<T, K extends string>( array: Array<T>, offset = 0, enumerationKey: K = 'position' ): Array<T & { [key: K]: number }> { return array.map((item, index) => ({ ...item, [enumerationKey]: offset + index, })); }
报错原因:
- 默认值
'position'是string类型,而泛型K是string的子类型(可能是具体字面量),TS无法将宽泛的string赋值给泛型K。 - 返回类型中的索引签名
{ [key: K]: number }不允许使用泛型参数作为索引类型,TS明确提示要用映射类型替代。
第二次尝试
function enumerate<T>( array: Array<T>, offset = 0, enumerationKey = 'position' ): Array<T & { [key: typeof enumerationKey]: number }> { return array.map((item, index) => ({ ...item, [enumerationKey]: offset + index, })); }
问题在于:没有给enumerationKey添加字面量类型约束,TS默认将其推断为宽泛的string类型,导致返回类型中的键也是string,无法精确对应传入的自定义键名。
正确的TypeScript实现
function enumerate<T, K extends string = 'position'>( array: T[], offset: number = 0, enumerationKey: K = 'position' as K ): Array<T & { [P in K]: number }> { return array.map((item, index) => ({ ...item, [enumerationKey]: offset + index, })) as Array<T & { [P in K]: number }>; }
关键细节解释
- 泛型默认值:给
K设置默认类型'position',当不传第三个参数时,K自动推断为字面量类型'position'。 - 默认值类型断言:
'position' as K解决了默认值与泛型K的类型不匹配问题,告诉TS默认值符合K的类型约束。 - 映射类型替代索引签名:用
{ [P in K]: number }替代原索引签名,这是TS允许的泛型索引写法,能精确关联自定义键名的字面量类型。 - 返回值类型断言:由于TS对
map返回值的类型推断不够精确,用类型断言明确返回值类型,确保类型匹配。
使用示例
// 默认键名,返回元素带position属性 const list1 = enumerate([{ name: 'Alice' }, { name: 'Bob' }]); // list1类型:Array<{ name: string; position: number }> // 自定义键名"idx",偏移量设为1 const list2 = enumerate([{ name: 'Alice' }, { name: 'Bob' }], 1, 'idx'); // list2类型:Array<{ name: string; idx: number }> // 原元素已有同名属性时,TS会自动检测类型冲突 const list3 = enumerate([{ position: 'not a number' }]); // 类型错误:Type 'string' is not assignable to type 'number'
内容的提问来源于stack exchange,提问作者l0rn
相关产品推荐
相关产品推荐

