You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从函数参数推断管道式函数数组的类型?

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 13:57:19