如何将可变参数类型转换为笛卡尔积函数的指定返回类型?
实现带精确类型推导的笛卡尔积函数(支持枚举与数组)
完整修正代码
// 定义输入类型:枚举(对象)或数组 type EnumOrArray = Record<string, any> | any[]; // 工具类型:提取输入的元素类型(数组取元素,枚举取值类型) type ExtractEnumOrArrayElement<T> = T extends any[] ? T[number] : T extends Record<string, infer Value> ? Value : never; // 可变泛型递归推导笛卡尔积结果类型 type CartesianProduct<T extends readonly EnumOrArray[]> = T extends [infer First, ...infer Rest] ? First extends EnumOrArray ? [ExtractEnumOrArrayElement<First>, ...CartesianProduct<Rest>][] : never : []; export function cartesian<T extends readonly EnumOrArray[]>(...inputs: [...T]): CartesianProduct<T> { const itemGroups = inputs.map(input => { if (Array.isArray(input)) { return input; } else { // 过滤枚举的数字键(反向映射),只取原始枚举值 const originalKeys = Object.keys(input).filter(key => isNaN(Number(key))); return originalKeys.map(key => input[key]); } }); function generateCartesianProduct(groups: any[][], prefix: any[] = []): any[][] { if (!groups.length) return [prefix]; const firstGroup = groups[0]; const restGroups = groups.slice(1); let result: any[][] = []; firstGroup.forEach(item => { result = result.concat(generateCartesianProduct(restGroups, [...prefix, item])); }); return result; } return generateCartesianProduct(itemGroups) as CartesianProduct<T>; }
关键类型逻辑说明
工具类型
ExtractEnumOrArrayElement:- 对数组类型,直接取
T[number]获取数组元素的联合类型 - 对枚举(TS中枚举编译后是包含正向/反向映射的对象),取对象值的类型
Value,和代码中提取枚举原始值的逻辑对应
- 对数组类型,直接取
可变泛型与递归类型
CartesianProduct:- 使用
T extends [infer First, ...infer Rest]解构输入元组,递归推导每一项的元素类型 - 最终将每一项的元素类型组合成元组,再包裹成数组类型,实现
[Color, number, Size][]这样的精确返回类型
- 使用
函数参数的可变泛型:
- 用
...inputs: [...T]让TS将输入的多个参数推导为元组类型T,而非数组类型,确保每一项的类型都能被精准捕获
- 用
测试验证
enum Color { Red = 'red', Green = 'green', Blue = 'blue' } enum Size { Small = 1, Medium = 10, Large = 100 } const numbers = [1, 2] as const; // 用as const让数组元素类型被精确推导为1|2 const combos = cartesian(Color, numbers, Size); // 此时combos的类型为:[Color, 1 | 2, Size][]
扩展支持Set的准备
如果后续要支持Set类型,只需修改ExtractEnumOrArrayElement工具类型:
type ExtractEnumOrArrayElement<T> = T extends any[] ? T[number] : T extends Set<infer Value> ? Value : T extends Record<string, infer Value> ? Value : never;
同时在函数的itemGroups处理逻辑中加入Set的判断:
if (input instanceof Set) { return Array.from(input); }
内容的提问来源于stack exchange,提问作者Steven Lu
相关产品推荐
相关产品推荐

