前言
最近我在封装一些工具方法,在封装到柯里化函数的时候,发现柯里化函数的ts类型标注十分困难。尝试了很多办法都没办法实现,使用curry生成的新函数能带提示的输入多个参数。
效果
最后取了一个择中的办法,柯里化函数本身传递两个参数fn和args,第一次调用传达一个函数和N和参数,生成新的函数newFn,newFn中接收一个后续参数,带类型提示。
实现
/**
* @type T代表传入函数的类型
* @type First代表第一次进入的参数
*/
type CurryFunc<T, First extends any[]> = T extends (
...args: infer Args
) => infer R
? // eslint-disable-next-line @typescript-eslint/no-unused-vars
Args extends [...First, infer Mid, ...infer Tail]
? (v: Mid) => CurryFunc<T, [...First, Mid]>
: R
: T;
/**
* 柯里化函数 可根据传入函数的类型自动推导
* @param fn 需要柯里化的函数
* @param rest 需要柯里化的函数的初始参数
* @returns 返回一个被柯里化的新函数或者 需要柯里化的函数的返回值
* @tip 由于ts类型限制的原因 无法在一个函数被柯里化后,再次多次传参!所以如果第二次传参需要使用多参数形式 需要使用@ts-ignore
* @example
* function add(a: number, b: number, c: number, d: number, e: number) {
* return a + b + c;
* }
* curry(add)(1)(2)(3)(4)(5);
* curry(add, 1)(2)(3)(4)(5);
* curry(add, 1, 2)(3)(4)(5);
* curry(add, 1, 2, 3)(4)(5);
* curry(add, 1, 2, 3, 4)(5);
* curry(add, 1, 2, 3, 4, 5);
*/
export function curry<T extends (...args: any[]) => any, First extends any[]>(
fn: T,
...rest: First
): CurryFunc<T, First> {
return function (...args: any[]): any {
const currentArgs = [...rest, ...args];
return currentArgs.length >= fn.length
? fn(...currentArgs)
: curry(fn, ...currentArgs);
} as CurryFunc<T, First>;
}