TS 4.4下Pipe递归类型签名两类报错的原因排查与修复求助
问题背景
我需要使用基于递归类型签名构建的pipe与compose库,该库在TS 3.4版本可正常运行,但在TS 4.4版本触发类型不匹配错误(ts:2322)无法运行。此处pipe等价于lodash和fp-ts中的flow函数,需要保留其3.4版本原有行为,修复4.4版本的兼容性问题。
错误成因
1. Pipe递归类型自引用错误
TS 4.2+版本收紧了递归条件类型的解析规则,3.4版本中惰性求值的递归类型不会被提前校验终止性,4.4版本会在类型解析阶段就检测递归的可终止性。原Pipe类型的条件分支没有对拆分出的First参数做函数类型前置校验,TS无法确定递归的终止边界,提前抛出了递归解析错误。
2. 实现与PipeFn签名不匹配
原PipeFn的泛型约束...fns: Fns & Pipe<Fns> extends AnyFunction ? Fns : never在4.4版本中的类型收窄逻辑更新,TS无法自动将用户编写的实现函数(参数和返回值默认推导为unknown)和递归计算出的Pipe
修复方案
修复后的完整代码如下:
type ExtractFunctionArguments < Fn > = Fn extends ( ...args: infer P ) => any ? P : never type ExtractFunctionReturnValue<Fn> = Fn extends ( ...args: any[] ) => infer P ? P : never type BooleanSwitch<Test, T = true, F = false> = Test extends true ? T : F type AnyFunction = ( ...args: any[] ) => any export type AnyFunction1 = ( a: any ) => any type Arbitrary = 'It is now 1554792354 seconds since since Jan 01, 1970' type IsAny<O, T = true, F = false> = Arbitrary extends O ? any extends O ? T : F : F // 修复递归类型校验问题,调整条件判断顺序、增加函数类型前置校验 export type Pipe<Fns extends any[], IsPipe = true, PreviousFunction = void, InitalParams extends any[] = any[], ReturnType = any> = Fns extends [] ? ( ...args: InitalParams ) => ReturnType : Fns extends [infer First, ...infer Next] ? First extends AnyFunction ? PreviousFunction extends void ? Pipe<Next, IsPipe, First, ExtractFunctionArguments<First>, ExtractFunctionReturnValue<First> > : ReturnType extends ExtractFunctionArguments<First>[0] ? Pipe<Next, IsPipe, First, InitalParams, ExtractFunctionReturnValue<First> > : IsAny<ReturnType> extends true ? Pipe<Next, IsPipe, First, InitalParams, ExtractFunctionReturnValue<First> > : { ERROR: ['Return type ', ReturnType , 'does comply with the input of', ExtractFunctionArguments<First>[0]], POSITION: ['Position of problem for input arguments is at', Fns['length'], 'from the', BooleanSwitch<IsPipe, 'end', 'beginning'> , 'and the output of function to the ', BooleanSwitch<IsPipe, 'left', 'right'>], } : never : never export type PipeFn = <Fns extends [AnyFunction, ...AnyFunction1[]]>( ...fns: Fns extends Pipe<Fns> ? Fns : never ) => Pipe<Fns>; // 增加类型断言兜底实现与签名的匹配 export const pipe: PipeFn = (entry: AnyFunction, ...funcs: AnyFunction1[]) => (...arg: any[]) => funcs.reduce((acc, item) => item(acc), entry(...arg)) as unknown as PipeFn;
修复点说明:
- 调整Pipe类型的条件判断顺序,先判断Fns是否为空、再拆分Fns的首元素和剩余元素,同时增加
First extends AnyFunction的前置校验,让TS可以正确识别递归终止逻辑,避免递归解析错误 - 简化PipeFn的泛型约束写法,适配4.4版本的类型收窄规则
- 将实现的参数类型从unknown改为any,同时增加
as unknown as PipeFn的类型断言兜底,解决实现和签名不匹配的问题 - 移除了不必要的item.call调用,直接调用item(acc)逻辑和原有行为完全一致
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

