我正在尝试为以下功能编写类型:
const curry = (
f, arr = []
) => (...args) => (
a => a.length >= f.length ?
f(...a) :
curry(f, a)
)([...arr, ...args]);
我发现this neat article像这样为curry
创建了一个类型:
type Head<T extends any[]> =
T extends [any, ...any[]]
? T[0]
: never;
type Tail<T extends any[]> =
((...t: T) => any) extends ((_: any, ...tail: infer TT) => any)
? TT
: [];
type HasTail<T extends any[]> =
T extends ([] | [any])
? false
: true;
type Curry<P extends any[], R> =
(arg: Head<P>) => HasTail<P> extends true
? Curry<Tail<P>, R>
: R;
[我的问题是,此Curry
类型的签名与我的curry
函数的签名不匹配(或者我的TypeScript技能是否很差并且确实如此?)。此外,我不知道如何为与curry
类型匹配的Curry
编写实现。
[如何实现curry
的类型?使用Curry
类型的实现看起来如何?
这里的一个主要警告:Function.length
很奇怪;对于没有默认值或rest参数的函数,它只会表现良好。例如,根据您的TS编译器针对的JS版本,您可以针对以下代码获得不同的答案:
Function.length
因此,在使用任何依赖于函数参数长度的运行时反映的内容时,请记住这一点。
您的console.log(((arg = 1) => { }).length); // 0? 1?
功能可分配给curry()
类型定义,反之亦然。如果基础函数仍然需要参数,则每次调用Curry
类型定义时都只期望一个参数,而Curry
调用时将接受任意数量的参数。这意味着我们可以为curry
提供curry()
类型,但是编译器将限制您调用它的参数数量。它可能看起来像这样:
Curry
这里,我们实质上是放弃让编译器在实现内部进行任何实型检查;内部的所有事物或多或少都是function curry<P extends any[], R>(f: (...args: P) => R): Curry<Required<P>, R> {
const _curry = (
f: (...args: any) => any, arr: any[] = []
) => (...args: any) => (
a => a.length >= f.length ?
f(...a) :
_curry(f, a)
)([...arr, ...args]);
return _curry(f);
}
类型。您可能可以在那里获得更好的类型安全性,但并不能得到太多。编译器将无法验证添加到元组类型末尾的操作,最终您将在所有地方都使用any
。
type assertions位可能不是必需的;它仅取决于您希望看到带有可选参数的函数发生的情况。通常,在联合类型的任何函数上或使用其参数列表的长度可能不同的函数时,我会非常小心。
让我们确保编译器对正常使用感到满意:
Required<P>
看起来不错。
关于如何一次为TS键入function test(x: string, y: number, z: boolean) {
return z ? x : y;
}
const t0 = curry(test);
const t1 = t0("abc");
const t2 = t1(123);
const t3 = t2(true);
console.log(t3); // abc
const t4 = t2(false);
console.log(t4); // 123
的完整的可能多次多个参数的版本,它需要类型级别的元组级联,当前不直接支持该级联(curry()
)。您可以编写涉及递归条件类型的东西,但是由于也不直接支持递归类型(see microsoft/TypeScript#5453),因此我不建议在生产系统中使用它们。
或者您可以一次选择一些最大长度来支持,例如说三个参数,并编写一个适用于此的see microsoft/TypeScript#26980版本,但我不知道这是否真的值得:
Curry
尤其是因为该特定定义使用了type Curry3<P extends any[], R> = P extends [] ? R : (
((a0: Head<P>) => Curry3<Tail<P>, R>) & (
P extends [any] ? unknown : (
((a0: Head<P>, a1: Head<Tail<P>>) => Curry3<Tail<Tail<P>>, R>) & (
P extends [any, any] ? unknown : (
((a0: Head<P>, a1: Head<Tail<P>>, a2: Head<Tail<Tail<P>>>) =>
Curry3<Tail<Tail<Tail<P>>>, R>)
)
)
)
)
);
,可能无法在任何地方都很好地播放。
无论如何,希望能有所帮助;祝你好运!