如何为笛卡尔乘积函数使用TypeScript变量元组类型?

如何为笛卡尔乘积函数使用TypeScript变量元组类型?,typescript,cartesian-product,typescript4.0,variadic-tuple-types,Typescript,Cartesian Product,Typescript4.0,Variadic Tuple Types,TypeScript 4.0开始支持的概念是,一个很好的类型构造,可用于连接函数等。文档中的一个示例: type Arr = readonly any[]; function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] { return [...arr1, ...arr2]; } 我当前使用的实现没有使用可变元组类型,需要显式类型转换: const cartesian = <T

TypeScript 4.0开始支持的概念是,一个很好的类型构造,可用于连接函数等。文档中的一个示例:

type Arr = readonly any[];

function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
  return [...arr1, ...arr2];
}
我当前使用的实现没有使用可变元组类型,需要显式类型转换:

const cartesian = <T extends any[]>(...arr: any[][]): T[] =>
  arr.reduce<T[]>(
    (a, b) => a.flatMap<T>(c => b.map<T>(d => [...c, d] as T)),
    [[]] as T
  );

const product = cartesian<[number, string]>([1, 2, 3], ['a', 'b', 'c']);
const笛卡尔=(…arr:any[]):T[]=>
arr.reduce(
(a,b)=>a.flatMap(c=>b.map(d=>[…c,d]as T)),
[[]as T
);
常数积=笛卡尔([1,2,3],'a','b','c']);
我正在寻找一个没有显式类型转换的解决方案,我认为可变元组类型可能是这里合适的类型构造

问题


如何使用可变元组类型来推断笛卡尔积函数的类型?

我认为可变元组类型实际上并没有改进我们的键入方式。自从在3.1中添加了对的支持之后,输入这一点实际上是可能的(以前可能是可能的,但没有那么干净)

在实际实现中仍然需要类型断言,但调用站点将推断参数类型并生成正确的返回类型:

type MapCartesian<T extends any[][]> = {
  [P in keyof T]: T[P] extends Array<infer U>? U: never
}
const cartesian = <T extends any[][]>(...arr: T): MapCartesian<T>[] =>
  arr.reduce(
    (a, b) => a.flatMap(c => b.map(d => [...c, d] )),
    [[]] 
  ) as MapCartesian<T>[];

const product = cartesian([1, 2, 3], ['a', 'b', 'c']);

类型映射笛卡尔={
[P in keyof T]:T[P]扩展数组?U:从不
}
常量笛卡尔=(…arr:T):映射笛卡尔[]=>
arr.reduce(
(a,b)=>a.flatMap(c=>b.map(d=>[…c,d]),
[[]] 
)作为笛卡尔坐标[];
常数积=笛卡尔([1,2,3],'a','b','c']);

如果是笛卡尔积,我们应该使用集合而不是数组。输入和输出都是如此

function cartesian<X, Y>(setX: Set<X>, setY: Set<Y>): Set<[X, Y]> {
    const result = new Set<[X, Y]>();
    setX.forEach(x => setY.forEach(y => result.add([x, y])));
    return result;
}

const product = cartesian(new Set([1, 2, 3]), new Set(['a', 'b', 'c']));
笛卡尔函数(setX:Set,setY:Set):Set{
常量结果=新集合();
setX.forEach(x=>setY.forEach(y=>result.add([x,y]));
返回结果;
}
常数积=笛卡尔(新集合([1,2,3]),新集合(['a','b','c']);
编辑(在Nicky的评论之后):

我尝试将函数签名推广到任意数量的集合,但无法从数组切换到集合:

declare function cartesian<A extends Array<Array<any>>>(
    ...args: A): { [K in keyof A]: A[K] extends Array<any> ? A[K][number] : never }[];
const product = cartesian([1, 2, 3], ['a', 'b', 'c'], [true, false]); 
声明笛卡尔函数(
…args:A:{[K in keyof A]:A[K]扩展数组?A[K][number]:never}[];
常数积=笛卡尔([1,2,3],'a','b','c'],[true,false]);
但随后我仔细阅读了@Titian Cernicova Dragomir的答案,并进行了很好的类型推断,因此我使用了他的方法处理集合:

declare function cartesian<A extends Array<Set<any>>>(
    ...args: A): Set<{ [K in keyof A]: A[K] extends Set<infer T> ? T : never }>;
const product = cartesian(new Set([1, 2, 3]), new Set(['a', 'b', 'c']), 
    new Set([true, false])); 
声明笛卡尔函数(
…args:A):设置;
常数积=笛卡尔(新集合([1,2,3]),新集合(['a','b','c']),
新设置([正确,错误]);

谢谢你的回答,但不幸的是,这不是问题的重点。@Nicky好的,但是顺便问一下,你需要这两个“集合”的笛卡尔积吗?或者更多?我需要无限多的集合,所以它应该处理N个不同的输入(而不是两个)。对不起,我误解了原来的问题。但是@Titian Cernicova Dragomir提供的解决方案似乎很好,不是吗?真的很好的解决方案!我遇到了笛卡尔函数(…args:A):{[K in keyof A]:A[K]扩展数组?A[K][number]:never}[]适用于数组,但您的解决方案肯定更好,因为它允许输入“集合”为任何类型,而不一定是数组。
declare function cartesian<A extends Array<Set<any>>>(
    ...args: A): Set<{ [K in keyof A]: A[K] extends Set<infer T> ? T : never }>;
const product = cartesian(new Set([1, 2, 3]), new Set(['a', 'b', 'c']), 
    new Set([true, false]));