柯里化(Currying)是函数式编程中的一个重要概念,它指的是将一个多参数函数转换成一系列接收单个参数的函数,每次调用只处理一个参数,直到接收完所有参数并执行函数。
在 JavaScript 中,lodash 等函数工具库提供了 _.curry
方法。但今天我们不使用库,而是用 TypeScript 实现一个具备完整类型推导的通用 curry 函数。
graph TD A[原始函数] --> B[函数定义] A --> C[参数传递流程] B --> D["function f(a: A, b: B, c: C)"] C --> E["a: A"] C --> F["(b: B)"] C --> G["(c: C)"]
🔍 使用场景举例
举个例子,如果我们有这样一个函数:
1 | function a(age: number, name: string, gender: 0 | 1) { |
✅ 最终实现
这段代码的关键有两个部分:
- 类型定义:我们要让 TypeScript 能够自动推导每一层参数的类型;
- 运行时逻辑:要在每次调用时判断参数是否收集完,若未完成,则返回下一个函数。
完整代码如下:
1 | // 类型定义 |
注意:
as Curried<A, R>
是关键的一步类型断言,用于让最终返回值拥有正确的类型提示。
🧠 类型推导原理解析
我们来看 Curried
的类型定义:
1 | type Curried<A extends any[], R> = |
A
是一个参数数组类型,例如[number, string, boolean]
。- 如果是空数组,返回
() => R
。 - 如果是一个参数,返回
(x: 参数类型) => R
。 - 否则递归返回
(x: 第一个参数) => 柯里化剩下的参数
。
这就实现了类型层面上的“参数分发”。
🚀 使用示例
我们再回头看看最初的示例,看看 TypeScript 能否正确推导类型:
1 | function a(age: number, name: string, gender: 0 | 1) { |
TypeScript 全程都能正确提示参数类型 ✅。
🧩 补充思考:柯里化的意义?
柯里化的一个常见用途是参数复用和函数组合:
1 | const withAge18 = curried(18); |
这种“预设部分参数”的方式,在表单处理、配置函数、事件绑定等场景中很有用。
🧱 总结
- 我们实现了一个支持完整类型推导的 curry 函数;
- 利用了 TypeScript 的条件类型、递归类型等高级技巧;
- 在多个参数的函数中,也能逐层传参并得到良好的类型提示;
- 柯里化是函数式编程的重要组成部分,在实际开发中也有不少落地场景。
如果你觉得有用,不妨点赞 + 收藏,一起探索更多 TypeScript 魔法 ✨