如何实现支持泛型函数的TypeScript柯里化工具函数?
支持泛型函数的TypeScript柯里化工具实现
问题背景
现有一款柯里化工具函数无法处理泛型函数的柯里化需求,期望实现如下效果:
const example_curry = curry(<T>(name: string, int: number): Promise<T> => { return Promise.resolve(int as unknown as T); }); const named_curry = example_curry('name'); const curry_result = named_curry<string>(5); // 可在柯里化后的函数中指定泛型类型
原工具代码因未考虑泛型参数传递逻辑,无法支持上述用法。
解决方案
核心思路是让柯里化后的函数能够接收并传递原函数的泛型参数,以下是修改后的完整实现:
类型定义调整
/* eslint-disable @typescript-eslint/no-explicit-any */ // 生成可选参数元组,支持部分传参场景 type PartialTuple<TUPLE extends any[], EXTRACTED extends any[] = []> = TUPLE extends [infer NEXT_PARAM, ...infer REMAINING] ? PartialTuple<REMAINING, [...EXTRACTED, NEXT_PARAM?]> : [...EXTRACTED, ...TUPLE]; // 获取函数的可选参数元组类型 type PartialParameters<FN extends (...args: any[]) => any> = PartialTuple<Parameters<FN>>; // 计算剩余需要传入的参数类型 type RemainingParameters<PROVIDED extends any[], EXPECTED extends any[]> = EXPECTED extends [infer E1, ...infer EX] ? PROVIDED extends [infer P1, ...infer PX] ? P1 extends E1 ? RemainingParameters<PX, EX> : never : EXPECTED : []; // 柯里化函数类型:支持原函数的泛型参数传递 type CurriedFunction<PROVIDED extends any[], FN extends <T extends any[]>(...args: any[]) => any> = <T extends any[], NEW_ARGS extends PartialTuple<RemainingParameters<PROVIDED, Parameters<FN<T>>>>>( ...args: NEW_ARGS ) => CurriedFunctionOrReturnValue<PROVIDED, FN, T, NEW_ARGS>; // 判断返回值是柯里化函数还是原函数的返回值 type CurriedFunctionOrReturnValue< PROVIDED extends any[], FN extends <T extends any[]>(...args: any[]) => any, T extends any[], NEW_ARGS extends any[] > = RemainingParameters<[...PROVIDED, ...NEW_ARGS], Parameters<FN<T>>> extends [any, ...any[]] ? CurriedFunction<[...PROVIDED, ...NEW_ARGS], FN> : ReturnType<FN<T>>; // 兼容泛型与非泛型函数的统一类型 type CurryableFunction = | (<T extends any[]>(...args: any[]) => any) | ((...args: any[]) => any);
柯里化函数实现
export default function curry<FN extends CurryableFunction, STARTING_ARGS extends PartialParameters<FN>>( targetFn: FN, ...existingArgs: STARTING_ARGS ): FN extends <T extends any[]>(...args: any[]) => any ? CurriedFunction<STARTING_ARGS, FN> : 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>)); } as any; }
验证示例
使用修改后的工具,可实现期望的泛型柯里化效果:
const example_curry = curry(<T>(name: string, int: number): Promise<T> => { return Promise.resolve(int as unknown as T); }); const named_curry = example_curry('Alice'); const curry_result = named_curry<string>(123); // 类型为Promise<string> const curry_result_num = named_curry<number>(456); // 类型为Promise<number>
关键改动说明
- 重构
CurriedFunction类型,使其支持接收原函数的泛型参数并传递到参数、返回值类型计算中 - 新增
CurryableFunction类型,同时兼容泛型与非泛型函数的柯里化需求 - 调整返回值类型判断逻辑,确保泛型参数能正确传递到原函数的返回值
内容的提问来源于stack exchange,提问作者Jonathan Woolf
相关产品推荐
相关产品推荐

