TypeScript 杂记六 《柯里化》

本文详细介绍了 TypeScript 中柯里化的概念,并通过一个示例展示了柯里化的效果。柯里化能够将多参数函数转换为一系列单参数函数,便于组合和复用。文中还给出了柯里化函数的类型定义和实现,以及如何动态生成柯里化函数的思路。最后,通过测试案例展示了柯里化函数的使用方式。
摘要由CSDN通过智能技术生成

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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值