如何将 TypeScript 可变元组类型用于笛卡尔积函数?
How to utilise TypeScript Variadic Tuple Types for a Cartesian Product function?
TypeScript 4.0 开始支持 Variadic Tuple Types 的概念,这是一个很好的类型构造,可以用于例如串联函数。来自文档的示例:
type Arr = readonly any[];
function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
return [...arr1, ...arr2];
}
我感兴趣的是这种类型构造是否可以用来键入 Cartesian Product function. The function should then infer the (mixed) types from the arguments to produce its return type. So if I input [number[], string[]]
I would expect the output to be of type [number, string][]
. Multiple implementations for a Cartesian Product can be found in this thread,但是 none 是严格键入的。这是一个例子:
const cartesian =
(...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
我目前使用的一个实现不使用可变元组类型,需要显式类型转换:
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']);
我正在寻找一个没有显式类型转换的解决方案,我认为可变元组类型可能是这里合适的类型构造。
问题
如何使用可变元组类型来推断笛卡尔积函数的类型?
我不认为 Variadic Tuple Types 实际上改进了我们输入它的方式。自从在 3.1 中添加了对 mapping tuples 的支持以来,输入这个实际上是可能的(之前可能是可能的,但不是那么干净)。
您在实际实现中仍需要类型断言,但调用站点将推断参数类型并生成正确的 return 类型:
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']);
如果它是笛卡尔积,我们应该使用 Set 而不是数组。无论是输入还是输出。
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']));
编辑(在 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]);
但后来我仔细阅读了@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]));
TypeScript 4.0 开始支持 Variadic Tuple Types 的概念,这是一个很好的类型构造,可以用于例如串联函数。来自文档的示例:
type Arr = readonly any[];
function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
return [...arr1, ...arr2];
}
我感兴趣的是这种类型构造是否可以用来键入 Cartesian Product function. The function should then infer the (mixed) types from the arguments to produce its return type. So if I input [number[], string[]]
I would expect the output to be of type [number, string][]
. Multiple implementations for a Cartesian Product can be found in this thread,但是 none 是严格键入的。这是一个例子:
const cartesian =
(...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
我目前使用的一个实现不使用可变元组类型,需要显式类型转换:
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']);
我正在寻找一个没有显式类型转换的解决方案,我认为可变元组类型可能是这里合适的类型构造。
问题
如何使用可变元组类型来推断笛卡尔积函数的类型?
我不认为 Variadic Tuple Types 实际上改进了我们输入它的方式。自从在 3.1 中添加了对 mapping tuples 的支持以来,输入这个实际上是可能的(之前可能是可能的,但不是那么干净)。
您在实际实现中仍需要类型断言,但调用站点将推断参数类型并生成正确的 return 类型:
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']);
如果它是笛卡尔积,我们应该使用 Set 而不是数组。无论是输入还是输出。
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']));
编辑(在 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]);
但后来我仔细阅读了@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]));