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

如何在双泛型参数函数中透明处理联合类型?

TypeScript中处理Factory联合类型的runFactory调用问题

现有Factory<A, B>类型定义,以及用于执行它的runFactory函数。当尝试将runFactory应用于返回Factory<string, number> | Factory<number, string>联合类型的生成器输出时,TypeScript会抛出类型不兼容错误,尽管代码实际运行正常。

报错代码

type Factory<A, B> = { makeValue: () => A, process: (a: A) => B }

const createFactory = (): Factory<string,number> | Factory<number,string> =>
  Math.random() < 0.5 ? 
    { makeValue: () => document.title, process: (s: string) => s.length} : 
    { makeValue: () => Math.random(), process: (n: number) => `${n}`}

const runFactory = <A, B>({makeValue, process}: Factory<A, B>): B => process(makeValue())

console.log(runFactory(createFactory()))

错误提示

Argument of type 'Factory<string, number> | Factory<number, string>' is not assignable to parameter of type 'Factory<string, number>'.
  Type 'Factory<number, string>' is not assignable to type 'Factory<string, number>'.
    The types returned by 'makeValue()' are incompatible between these types.
      Type 'number' is not assignable to type 'string'.

限制条件

  • 不能在runFactory内部使用类型守卫,因为Factory的泛型参数是未知的,类型本身应该自包含逻辑;
  • 必须保持强类型,禁止使用any或unknown;
  • 单泛型参数的调用方式(如runFactory<string | number>(createFactory()))无法满足需求,必须同时处理两个泛型参数的关联关系。

解决方案

TypeScript无法自动对联合类型的Factory进行分布式类型推导,我们可以通过分布式条件类型让runFactory正确识别联合类型中每个分支的返回值:

type Factory<A, B> = { makeValue: () => A, process: (a: A) => B }

const createFactory = (): Factory<string, number> | Factory<number, string> =>
  Math.random() < 0.5 ? 
    { makeValue: () => document.title, process: (s: string) => s.length} : 
    { makeValue: () => Math.random(), process: (n: number) => `${n}`}

// 定义分布式条件类型,从Factory联合类型中提取返回值类型
type ExtractFactoryReturn<T> = T extends Factory<infer _, infer B> ? B : never;

// 修改runFactory的泛型定义,支持联合类型输入
const runFactory = <T extends Factory<any, any>>(factory: T): ExtractFactoryReturn<T> => {
  const { makeValue, process } = factory;
  // 这里的类型断言是安全的,因为ExtractFactoryReturn已经正确推导了类型
  return process(makeValue()) as ExtractFactoryReturn<T>;
}

// 此时调用会自动推导返回值为 string | number
console.log(runFactory(createFactory()))

另一种更简洁的重载写法,同样可以实现强类型推导:

// 重载签名:处理单个Factory类型
function runFactory<A, B>(factory: Factory<A, B>): B;
// 重载签名:处理Factory联合类型
function runFactory<T extends Factory<any, any>>(factory: T): 
  T extends Factory<infer _, infer B> ? B : never;
// 实现逻辑
function runFactory(factory: Factory<any, any>) {
  return factory.process(factory.makeValue())
}

console.log(runFactory(createFactory()))

这两种方案都不需要类型守卫或any/unknown,同时保持了强类型检查,TypeScript会正确推导最终返回值为string | number,符合代码的实际运行逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 19:23:13