typescript实现curring,柯里化函数的类型标注

406 阅读1分钟

前言

最近我在封装一些工具方法,在封装到柯里化函数的时候,发现柯里化函数的ts类型标注十分困难。尝试了很多办法都没办法实现,使用curry生成的新函数能带提示的输入多个参数。

效果

最后取了一个择中的办法,柯里化函数本身传递两个参数fnargs,第一次调用传达一个函数和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>;
}