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

如何使用泛型定义支持异步操作的简易Pipe函数?

实现带类型推导的异步/同步兼容Pipe函数

你的核心问题是单个泛型无法覆盖管道中每一步的类型传递——管道里的每个步骤要么是直接值(会覆盖前一步结果),要么是函数(接收前一步的解包值,返回新结果),需要用递归泛型+条件类型来实现准确的类型推断。

完整实现代码

// 辅助类型:解包Promise类型,获取内部值的类型
type Unwrap<T> = T extends Promise<infer U> ? U : T;

// 递归类型:计算管道执行后的最终返回类型
type PipeReturn<Args extends any[]> = Args extends [infer First]
  ? First // 只有一个参数时,直接返回该参数的类型
  : Args extends [...infer Rest, infer Last]
  ? Last extends (value: Unwrap<PipeReturn<Rest>>) => infer Output
    ? Output // 最后一步是函数时,返回函数的输出类型
    : Last // 最后一步是值时,返回该值的类型
  : never;

export const pipe = <Args extends any[]>(...args: Args): PipeReturn<Args> => {
  return args.reduce((prev, exec) => {
    if (typeof exec !== 'function') {
      // 非函数值直接覆盖前一步结果
      return exec;
    }
    // 处理异步/同步逻辑:如果前一步是Promise,用then链式调用;否则直接执行函数
    return prev instanceof Promise ? prev.then(exec) : exec(prev);
  }) as PipeReturn<Args>;
};

代码解释

  1. Unwrap类型:用来提取Promise内部的类型,比如Unwrap<Promise<number>>会得到number,非Promise类型则直接返回自身。
  2. PipeReturn类型:递归推导管道的最终返回类型:
    • 如果只有一个参数,直接返回该参数的类型;
    • 如果有多个参数,先推导前面所有步骤的结果类型,再判断最后一步是函数还是值:
      • 是函数时,函数的参数类型为前面步骤结果的解包类型,返回函数的输出类型;
      • 是值时,直接返回该值的类型。
  3. pipe函数逻辑:简化了原实现的异步处理,直接通过instanceof Promise判断前一步结果类型,避免了多余的async函数嵌套,同时保持原有的行为。
  4. 类型断言:由于TypeScript对reduce的递归类型推断能力有限,最后用as PipeReturn<Args>断言返回类型,确保类型推导准确。

测试示例

// 示例1:纯值传递
const res1 = pipe(1, "hello", true);
// res1类型:boolean

// 示例2:同步函数
const res2 = pipe(10, (x) => x * 2);
// res2类型:number(值为20)

// 示例3:异步函数
const res3 = pipe(5, (x) => Promise.resolve(x + 3));
// res3类型:Promise<number>(值为8)

// 示例4:混合异步值和函数
const res4 = pipe(Promise.resolve(100), (x) => x / 2);
// res4类型:Promise<number>(值为50)

// 示例5:混合值和函数
const res5 = pipe("test", "hello world", (str) => str.length);
// res5类型:number(值为11)

内容的提问来源于stack exchange,提问作者sir-haver

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 02:01:03