TypeScript 杂记六 《柯里化》

Posted 左手121

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TypeScript 杂记六 《柯里化》相关的知识,希望对你有一定的参考价值。

TypeScript 杂记六 《柯里化》

柯里化

说明

简单通过一个示例说明柯里化的结果

const add = (a: number, b: number, c: number) => a + b + c;
add(1, 1, 1);
// 柯里化之后的效果
const curriedAdd = DynamicParamsCurrying(add);
curriedAdd(1, 2, 3);
curriedAdd(1, 2)(4);
curriedAdd(2)(3)(4);

思路

  • 简单定义一个主函数
declare function DynamicParamsCurrying<T extends unknown[], R>(
  func: (...args: T) => R
): CurriedFunction<T, R>;
  • 具体的 CurriedFunction 实现

    • 先假设固定 3 个参数则定义如下,如下示例:
    interface Func1<T1, R> 
      (): Func1<T1, R>;
      (t1: T1): R;
    
    interface Func2<T1, T2, R> 
      (): Func2<T1, T2, R>;
      (t1: T1): Func1<T2, R>;
      (t1: T1, t2: T2): R;
    
    interface Func3<T1, T2, T3, R> 
      (): Func3<T1, T2, T3, R>;
      (t1: T1): Func2<T2, T3, R>;
      (t1: T1, t2: T2): Func1<T3, R>;
      (t1: T1, t2: T2, t3: T3): R;
    
    interface Curry 
      <T1, T2, T3, R>(func: (t1: T1, t2: T2, t3: T3) => R): Func3<T1, T2, T3, R>;
    
    
    • 根据上述我们思考一下如何动态生成

      • 如果参数传完了则返回对应的结果
      • 如果不传参数,则重新来一次
      type CurriedFunction<T extends unknown[], R> = T extends []
        ? R
        :  (): CurriedFunction<T, R>  & CurriedDynamicFunction<T, R>;
      
      • 动态生成每一次递归的所有的函数,每次可以传递 1 - N 个参数
      type CurriedDynamicFunction<
        T extends unknown[],
        R,
        H extends unknown[] = []
      > = T extends [infer L, ...infer N]
        ? 
            // 第一次传入第一个参数
            (...args: [...H, L]): CurriedFunction<N, R>;
           & (N extends []
            ? // 如果没有更多的参数则停止
              
            : // 剔出第一个参数,传入下一次
              // 并将这个值存到 H 中,这样第二次上边传入的参数就是 2个
              // 依次类推
              CurriedDynamicFunction<N, R, [...H, L]>)
        : R;
      
  • 考虑如果没有入参的情况下,改动一下主函数

declare function DynamicParamsCurrying<T extends unknown[], R>(
  func: (...args: T) => R
): T extends [] ?  (): R  : CurriedFunction<T, R>;

最终结果

type CurriedDynamicFunction<
  T extends unknown[],
  R,
  H extends unknown[] = []
> = T extends [infer L, ...infer N]
  ? 
      (...args: [...H, L]): CurriedFunction<N, R>;
     & (N extends [] ?  : CurriedDynamicFunction<N, R, [...H, L]>)
  : R;

type CurriedFunction<T extends unknown[], R> = T extends []
  ? R
  :  (): CurriedFunction<T, R>  & CurriedDynamicFunction<T, R>;

declare function DynamicParamsCurrying<T extends unknown[], R>(
  func: (...args: T) => R
): T extends [] ?  (): R  : CurriedFunction<T, R>;

测试

  • 为了方便测试结果我们先去掉不传入参数的情况,改动一下 CurriedDynamicFunction 和 DynamicParamsCurrying
type CurriedDynamicFunction<
  T extends unknown[],
  R,
  H extends unknown[] = []
> = T extends [infer L, ...infer N]
  ? 
      (...args: [...H, L]): CurriedDynamicFunction<N, R>; // 这里变一下
     & (N extends [] ?  : CurriedDynamicFunction<N, R, [...H, L]>)
  : R;

declare function DynamicParamsCurrying<T extends unknown[], R>(
  func: (...args: T) => R
): T extends [] ?  (): R  : CurriedDynamicFunction<T, R>; // 这里变一下

const test1 = DynamicParamsCurrying(() => 0);
// 返回的类型: () => number;

const test2 = DynamicParamsCurrying((a: number) => a);
// 返回的类型: (args_0: number) => number;

const test3 = DynamicParamsCurrying((a: number, b: number) => a + b);
/* 

返回的类型: 
((args_0: number) => (args_0: number) => number) &
((args_0: number, args_1: number) => number);

*/
  • 不改动的效果
const test3 = DynamicParamsCurrying((a: number, b: number) => a + b);
const a = test3(1, 2);
const b = test3(1)(2);
const c = test3()()()(1)()()()(2);

以上是关于TypeScript 杂记六 《柯里化》的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 杂记六 《柯里化》

TypeScript 杂记十二 《Multiply》

TypeScript 杂记九 《Integers Comparator》

TypeScript 杂记九 《Integers Comparator》

TypeScript 杂记九 《Integers Comparator》

TypeScript 杂记一