如何在TypeScript中编写IndexOf<Array, T>类型?
实现TypeScript中readonly数组的元素索引反向映射类型
我需要基于一个包含字符串和数字的readonly数组,创建类似enum反向映射的对象,核心是实现IndexOf<A extends readonly any[], T extends A[number]>类型来获取指定元素在数组中的索引。示例代码如下:
const myArray = [1, '1+', 2, 3, '3+'] as const; type MyArrayIndexes = { [K in typeof myArray[number]]: IndexOf<typeof myArray, K>; }; // 期望正确通过类型检查的反向映射对象 const myArrayIndexes: MyArrayIndexes = { 1: 0, '1+': 1, 2: 2, 3: 3, '3+': 4, };
最初的IndexOf实现会导致所有索引值被推断为never,无法满足需求:
type IndexOf<A extends readonly unknown[], T extends A[number]> = Extract< { [K in keyof A & number]: [A[K], K]; }[keyof A & number], [T, number] >[1];
问题原因分析
原实现中,{[K in keyof A & number]: [A[K], K]}生成了一个以数组字面量索引为键的对象,但通过[keyof A & number]索引后得到的是所有[元素值, 字面量索引]的联合类型。而Extract的第二个参数写的是[T, number]——这里的number是宽泛的数字类型,无法匹配联合类型中具体的字面量索引(比如0、1),导致Extract返回never,最终索引类型也变成never。
解决方案
方案一:递归遍历匹配(支持重复元素时返回第一个索引)
通过递归遍历数组元素,逐个匹配目标类型,匹配成功时返回当前累加的索引长度:
type IndexOf< A extends readonly unknown[], T extends A[number], Acc extends unknown[] = [] > = A extends [infer First, ...infer Rest] ? First extends T ? Acc['length'] : IndexOf<Rest, T, [...Acc, First]> : never;
方案二:改进映射类型的匹配条件
修改Extract的匹配目标,将宽泛的number替换为数组的具体字面量索引类型keyof A & number,让匹配更精准:
type IndexOf<A extends readonly unknown[], T extends A[number]> = Extract< { [K in keyof A & number]: [A[K], K]; }[keyof A & number], [T, keyof A & number] >[1];
效果验证
使用上述任意一种方案后,MyArrayIndexes会被正确推断为:
type MyArrayIndexes = { 1: 0; '1+': 1; 2: 2; 3: 3; '3+': 4; }
此时myArrayIndexes的赋值可以顺利通过TypeScript的类型检查,实现预期的反向映射效果。
内容的提问来源于stack exchange,提问作者dx_over_dt
相关产品推荐
相关产品推荐

