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

为何TypeScript中pipe调用一处报错另一处正常?修改类型定义后恢复正常的原因

TypeScript pipe函数类型差异问题解析

在以下代码中,pipe(g, f)会触发类型错误,但pipe(g, h)却能正常通过类型检查:

<T, Fn extends (x: T) => number>(f: Fn) => {
  const g = (x: T) => x;
  const h = (_: T) => 1;
  pipe(g, f);  // 此处出现类型错误
  pipe(g, h);  // 类型检查正常通过
};

初始pipe类型定义

export type Func = (..._: any[]) => unknown;

export type Unary<Input, Output> = (_: Input) => Output;

export type Tail<L extends unknown[]> = L extends
  readonly [unknown, ...infer LTail] ? LTail
  : L;

type UnaryFn<A, R> = (a: A) => R;
type Arg<F> = F extends UnaryFn<infer A, unknown> ? A : never;
type Res<F> = F extends UnaryFn<any, infer R> ? R : never;

type ValidCompose<F1, F2> = Res<F1> extends (Arg<F2> | Promise<Arg<F2>>) ? F1
  : (arg: Arg<F1>) => Arg<F2>;

type ValidPipe<FS> = FS extends [infer F1, infer F2, ...infer Rest]
  ? [ValidCompose<F1, F2>, ...ValidPipe<[F2, ...Rest]>]
  : FS;

export const pipe = <Fs extends Func[]>(...fs: ValidPipe<Fs>) => null

修复后的类型定义

修改以下部分后,所有pipe调用都能正常通过类型检查:

type Arg<F extends Func> = Parameters<F>[0];
type Res<F> = F extends UnaryFn<any, infer R> ? R : never;

type ValidCompose<F1 extends Func, F2 extends Func> = Res<F1> extends
  (Arg<F2> | Promise<Arg<F2>>) ? F1
  : (...arg: Parameters<F1>) => Arg<F2>;

type ValidPipe<FS extends Func[]> = FS extends
  [infer F1 extends Func, infer F2 extends Func, ...infer Rest extends Func[]]
  ? [ValidCompose<F1, F2>, ...ValidPipe<[F2, ...Rest]>]
  : FS;

问题原因与修复逻辑

初始代码的问题点

  1. Arg<F>类型推断能力不足
    初始的Arg<F>通过自定义的UnaryFn类型推断参数:F extends UnaryFn<infer A, unknown> ? A : never。但这个逻辑只能处理非泛型的一元函数,当F是带泛型约束的函数类型(比如示例中的Fn extends (x: T) => number),TypeScript无法准确推断出A的具体类型,导致Res<g>(即T)与Arg<f>的类型匹配失败。

而h是明确的非泛型一元函数,Arg<h>能正确推断为T,所以pipe(g, h)能正常工作。

  1. 缺乏明确的类型约束
    初始的ValidCompose和ValidPipe没有强制泛型参数必须是Func类型,递归处理时TypeScript对函数类型的推断存在模糊性,进一步加剧了类型不匹配的问题。

修改后的修复原理

  1. 使用内置工具类型提升推断准确性
    用Parameters<F>[0]替代自定义的Arg<F>:Parameters是TypeScript内置工具类型,能直接提取函数的参数元组,无论函数是否带泛型约束,都能准确获取第一个参数的类型。对于示例中的Fn,Parameters<Fn>[0]能正确得到T,解决了初始Arg的推断盲区。

  2. 添加明确的类型约束
    给ValidCompose的F1、F2,以及ValidPipe的F1、F2、Rest都添加extends Func的约束,强制所有参与类型计算的元素都是函数类型,让TypeScript在递归处理时能明确类型边界,避免模糊推断。

  3. 兼容完整参数列表
    将初始的(arg: Arg<F1>) => Arg<F2>改为(...arg: Parameters<F1>) => Arg<F2>,确保返回函数的参数完全匹配原始函数的参数结构,不仅提升了类型安全性,还兼容多参数函数场景。


内容的提问来源于stack exchange,提问作者Uri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:34:50