现在,正确键入此内容的最大障碍是 TypeScript 无法从 TypeScript 3.0 开始连接或拆分元组。有这样做的建议,TypeScript 3.1 及更高版本可能正在开发中,但现在还没有。到今天为止,您所能做的就是枚举最大有限长度的案例,或者尝试欺骗编译器使用不推荐的递归。
如果我们想象有一个TupleSplit<T extends any[], L extends number>
类型函数可以接受一个元组和一个长度并将该长度的元组拆分为初始组件和其余部分,这样TupleSplit<[string, number, boolean], 2>
就会产生{init: [string, number], rest: [boolean]}
,那么您可以将curry
函数的类型声明为如下所示:
declare function curry<A extends any[], R>(
f: (...args: A) => R
): <L extends TupleSplit<A, number>['init']>(
...args: L
) => 0 extends L['length'] ?
never :
((...args: TupleSplit<A, L['length']>['rest']) => R) extends infer F ?
F extends () => any ? R : F : never;
为了能够尝试,让我们介绍一个TupleSplit<T, L>
仅适用于L
最多的版本3
(您可以根据需要添加)。它看起来像这样:
type TupleSplit<T extends any[], L extends number, F = (...a: T) => void> = [
{ init: [], rest: T },
F extends ((a: infer A, ...z: infer Z) => void) ?
{ init: [A], rest: Z } : never,
F extends ((a: infer A, b: infer B, ...z: infer Z) => void) ?
{ init: [A, B], rest: Z } : never,
F extends ((a: infer A, b: infer B, c: infer C, ...z: infer Z) => void) ?
{ init: [A, B, C], rest: Z } : never,
// etc etc for tuples of length 4 and greater
...{ init: T, rest: [] }[]
][L];
curry
现在我们可以测试一个函数的声明,比如
function add(x: number, y: number) {
return x + y;
}
const curriedAdd = curry(add);
const addTwo = curriedAdd(2); // (y: number) => number;
const four = curriedAdd(2,2); // number
const willBeAnError = curriedAdd(); // never
这些类型在我看来是正确的。
当然,这并不意味着 的实现会对curry
这种类型感到满意。您可能可以像这样实现它:
return <L extends TupleSplit<A, number>['init']>(...args: TupleSplit<A, L['length']>['rest']) => {
if (args.length === 0) {
throw new Error("Empty invocation")
} else if (args.length < f.length) {
return curry(f.bind(null, ...args))
} else {
return f(...args as A)
}
}
可能。我没有测试过。
无论如何,希望这是有道理的,并给你一些方向。祝你好运!
更新
curry()
如果您不传入所有参数,我没有注意到返回更多柯里化函数的事实。这样做需要递归类型,如下所示:
type Curried<A extends any[], R> =
<L extends TupleSplit<A, number>['init']>(...args: L) =>
0 extends L['length'] ? never :
0 extends TupleSplit<A, L['length']>['rest']['length'] ? R :
Curried<TupleSplit<A,L['length']>['rest'], R>;
declare function curry<A extends any[], R>(f: (...args: A)=>R): Curried<A, R>;
function add(x: number, y: number) {
return x + y;
}
const curriedAdd = curry(add);
const addTwo = curriedAdd(2); // Curried<[number], number>
const three = addTwo(1); // number
const four = curriedAdd(2,2); // number
const willBeAnError = curriedAdd(); // never
这更像是原始定义。
但我也注意到,如果你这样做:
const wat = curriedAdd("no error?"); // never
它没有得到错误,而是返回never
. 这对我来说看起来像是一个编译器错误,但我还没有跟进它。编辑:好的,我为此提交了Microsoft/TypeScript#26491。
干杯!