typescript 中柯里化函数类型靠递归条件类型+元组展开自动推导,通过 [infer head, ...infer tail] 拆解参数元组,空元组 [] 为终止条件,支持任意参数个数、重载、默认/可选参数,并需配合运行时 curry 函数实现类型与行为一致。

在 TypeScript 中为柯里化函数定义类型,关键不是“手动写一堆嵌套箭头函数”,而是让类型系统能自动推导每一步的参数和返回值——靠的是递归条件类型 + 元组展开,而不是硬编码。
用递归条件类型建模“逐步消耗参数”
把原函数的参数类型看作一个元组 T extends any[],每次取第一个参数,剩余部分构成新元组,直到为空。TypeScript 4.7+ 支持元组模式匹配(如 [Head, ...Tail]),配合 infer 就能安全拆解:
-
T extends [infer Head, ...infer Tail]:非空元组,提取首项和剩余项 -
T extends []:空元组,作为递归终止条件,直接返回结果类型R - 避免无限递归:必须明确写出空元组分支,否则类型会持续展开报错
定义通用 Curry 类型
假设原始函数类型是 (...args: T) => R,那么柯里化后应表现为:
接收 T[0] → 返回能接收 T[1] 的函数 → …… → 最终返回 R。
典型实现如下:
type Curry<f> = F extends (...args: infer T) => infer R
? T extends []
? () => R
: T extends [infer Head, ...infer Tail]
? (arg: Head) => Curry R>
: never
: never;</f>
- 它不依赖具体参数个数,支持任意长度元组(包括 0、1、5 个参数)
- 编辑器在调用时能逐层提示下一个参数类型,比如
curriedFn(123)后,下一步只接受string或boolean(取决于Tail的推导结果) - 对重载函数、含默认参数、可选参数的函数也兼容,只要元组类型能被准确提取
配合运行时 curry 函数使用
光有类型不行,还得让实际函数行为匹配。常见做法是导出一个带泛型约束的 curry 工具函数:
function curry<f extends any> any>(fn: F): Curry<f> {
return function curried(...args: any[]): any {
return args.length >= fn.length
? fn(...args)
: (...nextArgs: any[]) => curried(...args, ...nextArgs);
} as any;
}</f></f>
- 返回值强制断言为
Curry<f></f>,把运行时逻辑和类型定义桥接起来 - 调用
curry(sum)后,TypeScript 能根据sum的签名(如(a: number, b: number, c: number) => number)自动推导出三层嵌套类型 - 搭配 VS Code 使用时,鼠标悬停能看到完整类型链:
(a: number) => (b: number) => (c: number) => number
注意边界情况处理
真实项目中常遇到这些细节,类型定义需提前覆盖:
-
含 this 上下文的函数:需在类型中保留
this参数,用ThisParameterType<f></f>提取并透传 -
泛型函数:Curry 类型本身也要支持泛型,例如
Curry(x: T) => T>,此时需用ReturnType和更深层 infer 配合 -
占位符(如 _)支持:类型上需额外建模“跳过某位参数”的状态,通常引入联合类型
Placeholder | Arg并在递归中过滤
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











