柯里化函数类型

Tags
typescript
Created
Aug 3, 2022 11:09 AM

https://medium.com/codex/currying-in-typescript-ca5226c85b85

// 函数范型,部分开始参数
function curry<FN extends (...args: any[]) => any, STARTING_ARGS extends PartialParameters<FN>>(
  targetFn: FN,
  ...existingArgs: STARTING_ARGS
): CurriedFunction<STARTING_ARGS, FN> {
  return function (...args) {
    const totalArgs = [...existingArgs, ...args];
    if (totalArgs.length >= targetFn.length) {
      return targetFn(...totalArgs);
    }
    return curry(targetFn, ...(totalArgs as PartialParameters<FN>));
  };
}




// 柯里化后的返回值,参数(NEW_ARGS)是剩余参数的一部分,如果参数不够则需要迭代
type CurriedFunction<PROVIDED extends any[], FN extends (...args: any[]) => any> = <
  NEW_ARGS extends PartialTuple<RemainingParameters<PROVIDED, Parameters<FN>>>,
>(
  ...args: NEW_ARGS
) => CurriedFunctionOrReturnValue<[...PROVIDED, ...NEW_ARGS], FN>%3B





// 没有剩余参数时返回函数返回值,否则返回多了已有参数的柯里化函数

type CurriedFunctionOrReturnValue<PROVIDED extends any[], FN extends (...args: any[]) => any> = RemainingParameters<
  PROVIDED,
  Parameters<FN>
> extends [any, ...any[]]
  ? CurriedFunction<PROVIDED, FN>
  : ReturnType<FN>%3B




// 转换为可选元组

type PartialTuple<TUPLE extends any[], EXTRACTED extends any[] = []> =
  // If the tuple provided has at least one required value
  TUPLE extends [infer NEXT_PARAM, ...infer REMAINING]
    ? // recurse back in to this type with one less item
      // in the original tuple, and the latest extracted value
      // added to the extracted list as optional
      PartialTuple<REMAINING, [...EXTRACTED, NEXT_PARAM?]>
    : // else if there are no more values,
      // return an empty tuple so that too is a valid option
      [...EXTRACTED, ...TUPLE];




// 获取部分参数(可选)

type PartialParameters<FN extends (...args: any[]) => any> = PartialTuple<Parameters<FN>>%3B





// 获取剩余参数,提供的参数类型必须符合期望参数类型
type RemainingParameters<PROVIDED extends any[], EXPECTED extends any[]> =
  // if the expected array has any required items…
  EXPECTED extends [infer E1, ...infer EX]
    ? // if the provided array has at least one required item
      PROVIDED extends [infer P1, ...infer PX]
      ? // if the type is correct, recurse with one item less
        //in each array type
        P1 extends E1
        ? RemainingParameters<PX, EX>
        : // else return this as invalid
          never
      : // else the remaining args is unchanged
        EXPECTED
    : // else there are no more arguments
      [];
SuperMade with Super