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

TypeScript:合并联合类型的泛型参数

解决TypeScript中Foo接口bar方法泛型参数自动合并的问题

我希望bar方法能自动合并Foo的泛型类型参数,否则调用baz方法会抛出如下错误:

Each member of the union type '(<TBaz extends string>(value: TBaz) => void) | (<TBaz extends number>(value: TBaz) => void)' has signatures, but none of those signatures are compatible with each other.ts(2349)

复现错误的代码

interface Foo<TX, TY> {
  bar<TFoo extends Foo<unknown, unknown>>(mapper: (value: TX) => TFoo): TFoo;
  baz<TBaz extends TY>(value: TBaz): void
}

const x = null as unknown as Foo<'1', number>;

const y = x.bar(x => {
  if(x) {
    return null as unknown as Foo<'2', number>;
  } else {
    return null as unknown as Foo<'3', string>;
  }
});

// y 的类型为 Foo<'2', number> | Foo<'3', string>
// 但我们需要的类型是 Foo<'2' | '3', number | string>

const z = y.baz('2');
// 这里会触发类型错误,虽然运行逻辑没问题

手动指定泛型Foo<'2'|'3',string|number>可以规避错误,但我希望TypeScript自动处理,无需每次显式指定。之前尝试用infer提取泛型参数重新构造类型,但没成功:

bar<TFoo extends Foo<unknown, unknown>>(mapper: (value: TX) => TFoo): TFoo extends Foo<infer X, infer Y> ? Foo<X, Y> : never;

解决方案

要实现自动合并泛型参数,需要调整bar方法的返回类型,让TypeScript把TFoo联合类型里的泛型参数分别提取出来,再合并成联合类型。可以通过分布式条件类型来实现:

方法一:定义类型工具复用逻辑

先写一个类型工具,专门处理Foo联合类型的泛型合并:

type MergeFooUnion<T extends Foo<unknown, unknown>> = 
  Foo<
    // 提取所有Foo实例的TX参数并合并成联合类型
    T extends Foo<infer X, unknown> ? X : never,
    // 提取所有Foo实例的TY参数并合并成联合类型
    T extends Foo<unknown, infer Y> ? Y : never
  >;

然后修改Foo接口的bar方法,返回类型改成这个工具类型:

interface Foo<TX, TY> {
  bar<TFoo extends Foo<unknown, unknown>>(mapper: (value: TX) => TFoo): MergeFooUnion<TFoo>;
  baz<TBaz extends TY>(value: TBaz): void
}

方法二:直接内联类型逻辑

如果不想单独定义类型工具,也可以把合并逻辑直接写在bar的返回类型里:

interface Foo<TX, TY> {
  bar<TFoo extends Foo<unknown, unknown>>(
    mapper: (value: TX) => TFoo
  ): Foo<
    TFoo extends Foo<infer X, unknown> ? X : never,
    TFoo extends Foo<unknown, infer Y> ? Y : never
  >;
  baz<TBaz extends TY>(value: TBaz): void
}

测试效果

修改后再运行之前的代码:

const x = null as unknown as Foo<'1', number>;

const y = x.bar(x => {
  if(x) {
    return null as unknown as Foo<'2', number>;
  } else {
    return null as unknown as Foo<'3', string>;
  }
});

// y 的类型自动变为 Foo<'2' | '3', number | string>
const z = y.baz('2'); // 不再触发类型错误,类型推导正常

原理说明

分布式条件类型会自动遍历联合类型中的每个成员,分别提取X和Y参数,最终把所有提取到的X合并成一个联合类型,Y同理,再用这两个联合类型构造出新的Foo类型。这样bar的返回值就不再是Foo的联合类型,而是泛型参数合并后的单一Foo类型,调用baz时自然不会出现签名不兼容的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 14:17:46