gpt4 book ai didi

typescript - 如何在 Typescript 4 中编写 curry 和 compose?

转载 作者:行者123 更新时间:2023-12-05 08:20:00 28 4
gpt4 key购买 nike

经过variadic types之后我想做这个,但我想知道如何使用函数数组。

这是我的第一次尝试:

function curry<T extends any[]>(fn: (...args: T) => any) {
return function(...args: T) {
return args.length >= fn.length
? fn(...args)
: curry(fn.bind(undefined, ...args));
}
}

但是对于 fn.bind 我得到“'(...args: T) => any' 类型的 'this' 上下文不可分配给方法的 'this' 类型'(this: undefined, ...args: any[]) => any'。”

有什么想法吗?

最佳答案

你并没有真正使用 variadic tuple tpes在你的代码中。 curry()的具体 flavor 您正在实现的是一个采用部分参数列表然后可能返回另一个函数的函数,该函数采用列表的部分剩余部分。这意味着参数的初始元组 T可能会被分成许多不同的部分,因此不会像 partialCall() 那样从上下文中自动推断出来。文档中的函数即可。

相反,您需要明确打破 T将元组分成可能的子元组。让我们代表 curry() 的所需输出类型它接受一个函数,其参数是元组 A其返回类型为 R :

type Curried<A extends any[], R> =
<P extends Partial<A>>(...args: P) => P extends A ? R :
A extends [...SameLength<P>, ...infer S] ? S extends any[] ? Curried<S, R>
: never : never;

type SameLength<T extends any[]> = Extract<{ [K in keyof T]: any }, any[]>

让我把它翻译成英文。 Curried<A, R>是一个通用函数,其参数必须是某种元组类型 P限制为 Partial<A> .

对于元组,Partial<A>结束意味着你可以省略元组的任何后缀(从某个地方到最后)。所以[1, 2, 3]可分配给 Partial<[1,2,3,4,5,6,7]> ,但是[1, 2, 4]不是。 undefined 有点问题, 自 [1, undefined, 3]也可以分配给 Partial<[1,2,3,4,5,6,7]> ,但我会忽略它,如果它变得重要,它可以解决。无论如何,这意味着 Curried<A, R> 的参数必须是 A 的某个前缀(初始 block )元组。

Curried<A, R> 的返回类型取决于前缀 P传进来了。如果P是整个元组 A , 那么返回类型就是 R (这就是当您最终为函数提供所有参数时发生的情况)。否则,你 split A进入前缀 P及其后缀 S , 并返回一个类型为 Curried<S, R> 的新柯里化(Currying)函数.

拆分 A进入[...SameLength<P>, ...infer S]使用可变元组类型。注意 SameLength<P>只是一个与 P 长度相同的元组但其元素类型为 any .这避免了 P 的问题被推断为非常窄(假设 A[number, number, number],然后 P[0, 0]。您不能将 [number, number, string] 拆分为 [0, 0, ...infer S],因为 number 不可分配给 0。但我们只关心这里的长度,所以我们将 [number, number, string] 拆分为 [any, any, ...infer S] 并且这有效,并推断出 Sstring )。

好的,使用并实现它:

function curry<A extends any[], R>(fn: (...args: A) => R): Curried<A, R> {
return (...args: any[]): any =>
args.length >= fn.length ? fn(...args as any) : curry((fn as any).bind(undefined, ...args));
}

我用了很多 type assertionscurry() 的执行中,因为编译器在尝试验证返回的函数是否可分配给 Curried<A, R> 时几乎绝望地迷失了方向.与其担心它,我只是告诉编译器不要费心验证安全性,并自己负责使实现正确。 (如果错了,我怪我自己,而不是编译器)。

好的,我们开始吧。有用吗?

const fn = (a: string, b: number, c: boolean) => (a.length <= b) === c ? "yep" : "nope";

const cFn = curry(fn);
const val1 = cFn("")(1)(true);
console.log(val1); // yep

const val2 = cFn("", 1, true);
console.log(val2); // yep

const val3 = cFn()()()()("", 1)()()(true); // yep

我觉得不错。请注意,根据我的定义,如果您调用 Curried<A, R>没有参数你就得到一个 Curried<A, R> .以下是一些故意错误,因此您可以看到编译器捕获了它们:

// errors
cFn(1, 1, true); // error!
// ~ <-- not a string
cFn("", 1, true, false); // error!
// ~~~~~ <-- Expected 0-3 arguments, but got 4
cFn("")(1)(false)(true); // error!
//~~~~~~~~~~~~~~~ <-- This expression is not callable.

这些对我来说看起来像是正确的错误。


Playground link to code

关于typescript - 如何在 Typescript 4 中编写 curry 和 compose?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63903982/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com