如何从函数参数推断管道式函数数组的类型?
Pipe式函数数组的类型自动推断优化
我有一个类似pipe()工作模式的函数数组,数据会按顺序从第一个函数传入第二个,再传入第三个……直到最后一个。目前已经定义了Hooks类型来校验前一个函数的输出与后一个函数的输入类型匹配,但这个类型需要手动传入全部泛型参数,使用起来很繁琐。想请教:有没有办法让TypeScript自动从函数参数推断类型?或者有没有办法减少泛型参数的输入,同时保留函数数组的数量限制与类型匹配校验逻辑?
当前实现的Hooks类型:
type Hooks<T1, T2, T3, T4, T5, T6, T7, T8, T9> = [ (e: T1) => T2 ] | [ (e: T1) => T2, (e: T2) => T3 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4, (e: T4) => T5 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4, (e: T4) => T5, (e: T5) => T6 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4, (e: T4) => T5, (e: T5) => T6, (e: T6) => T7 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4, (e: T4) => T5, (e: T5) => T6, (e: T6) => T7, (e: T7) => T8 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4, (e: T4) => T5, (e: T5) => T6, (e: T6) => T7, (e: T7) => T8, (e: T8) => T9 ];
当前使用示例(需要手动传入大量泛型参数):
const example: Hooks<number, string, boolean, never, never, never, never, never, never> = [ e => e.toString(), (e: string) => Boolean(e), ];
期望的效果(语法无效,仅作示意):
type Hooks = [ (e: infer T1) => infer T2 ] | [ (e: T1) => T2, (e: T2) => infer T3 ] | [ (e: T1) => T2, (e: T2) => T3, (e: T3) => T4 ]; // 该语法不合法
解决方案:用递归可变元组类型实现自动推断
可以利用TypeScript的递归类型和可变元组类型,让TS自动推断每个函数的输入输出类型,同时保留类型匹配校验和数组长度限制。
1. 基础版:自动类型匹配(无长度限制)
// 定义单个pipe函数的类型 type PipeFn<I, O> = (input: I) => O; // 递归构建pipe函数数组类型,确保前一个函数的输出等于后一个的输入 type PipeHooks<T extends any[]> = // 单个函数的情况 T extends [PipeFn<infer _, infer _>] ? T : // 多个函数的情况:校验第一个函数的输出与第二个函数的输入匹配,递归校验剩余函数 T extends [PipeFn<infer I, infer O>, ...infer Rest] ? Rest extends PipeHooks<[PipeFn<O, infer _>, ...infer _]> ? T : never : never; // 封装一个创建函数,让TS自动推断类型 function createHooks<T extends PipeHooks<T>>(hooks: T) { return hooks; }
使用示例
// 自动推断类型,无需手动传泛型 const example = createHooks([ (e: number) => e.toString(), (e: string) => Boolean(e), ]); // 类型不匹配时会自动报错 const invalidExample = createHooks([ (e: number) => e.toString(), (e: number) => Boolean(e), // ❌ 错误:输入类型应为string,与前一个函数的输出不匹配 ]);
2. 进阶版:限制函数数组最多9个(和原需求一致)
如果需要保留原需求中最多9个函数的限制,可以给递归类型加上深度计数:
type PipeFn<I, O> = (input: I) => O; // Depth用于记录当前递归深度,限制最多9个函数 type PipeHooks<T extends any[], Depth extends number[] = []> = // 超过9个函数则返回never(报错) Depth['length'] extends 9 ? never : T extends [PipeFn<infer _, infer _>] ? T : T extends [PipeFn<infer I, infer O>, ...infer Rest] ? Rest extends PipeHooks<[PipeFn<O, infer _>, ...infer _], [...Depth, 0]> ? T : never : never; function createHooks<T extends PipeHooks<T>>(hooks: T) { return hooks; }
效果验证
// 9个函数:正常通过 const nineHooks = createHooks([ (e: number) => e.toString(), (e: string) => parseInt(e), (e: number) => e + 1, (e: number) => e.toString(), (e: string) => Boolean(e), (e: boolean) => e ? 1 : 0, (e: number) => e.toString(), (e: string) => e.length, (e: number) => e > 5, ]); // 10个函数:报错 const tenHooks = createHooks([ (e: number) => e.toString(), (e: string) => parseInt(e), (e: number) => e + 1, (e: number) => e.toString(), (e: string) => Boolean(e), (e: boolean) => e ? 1 : 0, (e: number) => e.toString(), (e: string) => e.length, (e: number) => e > 5, (e: boolean) => e.toString(), // ❌ 错误:数组长度超过限制 ]);
内容的提问来源于Stack Exchange,提问作者HardCoreQual
相关产品推荐
相关产品推荐

