TypeScript函数组合链式方法的类型推断问题求解
问题描述
我尝试实现一个工具函数,为指定函数直接扩展支持函数组合的可链式调用compose方法,核心代码如下:
const F = <T, U>(f: (a: T) => U) => { type F = { compose: <V,>(g: (b: U) => V) => (((a: T) => V) & F); }; return Object.defineProperty(f, "compose", { configurable: true, value: <V,>(g: (b: U) => V) => F((a: T) => g(f(a))) }) as ((a: T) => U) & F }; const f1 = (a: number) => a + 1; const f2 = (a: number) => a.toString(); const identity = <T,>(a: T) => a; const F2 = F(f2); // ((a: number) => string) & F // 符合预期 const F12 = F(f1).compose(f2); // ((a: number) => string) & F // 符合预期 const F2i = (F2).compose(identity); // ((a: number) => string) & F // 符合预期 const f12i = (F12).compose(identity); // ((a: number) => number) & F // 不符合预期,原因不明 const fi1 = F(identity).compose(f1); /* ts(2345) 报错 Argument of type '(a: number) => number' is not assignable to parameter of type '(b: unknown) => number'. Types of parameters 'a' and 'b' are incompatible. Type 'unknown' is not assignable to type 'number'. const f1: (a: number) => number */
现有待解决问题:
- 类型一致的
F2和F12调用compose传入identity时返回类型不一致,F12的返回类型不符合预期,无法定位原因 - 调用
F(identity).compose(f1)时报ts(2345)错误,无修复思路
注:需求为直接给原生函数挂载
compose方法,不允许将函数包裹在对象中
问题根因&修复方案
根因分析
- 类型不一致问题:你在
F函数内部定义的F类型没有和当前作用域的泛型参数T、U做强绑定,TypeScript在链式调用场景下无法正确携带累积的泛型信息,导致后续调用出现类型推断错误。 - identity调用报错问题:直接调用
F(identity)时,TypeScript无法自动推断泛型参数T的类型,默认回退为unknown,后续传入接收number入参的f1自然会报类型不兼容错误。
修复代码
将组合函数的类型抽离到全局作用域,明确绑定泛型参数,优化类型推断逻辑:
// 抽离全局的组合函数类型,明确泛型绑定关系 type ComposeFunction<T, U> = ((a: T) => U) & { compose: <V>(g: (b: U) => V) => ComposeFunction<T, V> } const F = <T, U>(f: (a: T) => U): ComposeFunction<T, U> => { return Object.defineProperty(f, "compose", { configurable: true, value: <V>(g: (b: U) => V) => F((a: T) => g(f(a))) }) as ComposeFunction<T, U> }; // 调用泛型函数时如果无法自动推断类型,显式传入泛型参数即可 const fi1 = F<number, number>(identity).compose(f1);
修复后验证
F2.compose(identity)返回类型为ComposeFunction<number, string>,符合预期F12.compose(identity)返回类型和前者完全一致,无类型错误- 显式指定
identity的泛型参数后,fi1无ts(2345)报错,类型完全符合预期
内容的提问来源于stack exchange,提问作者Functor
相关产品推荐
相关产品推荐

