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

使用工厂函数实现联合类型、鉴别符与Partial<T>的类型收窄

TypeScript 鉴别符工厂函数的类型安全实现

问题背景

需要实现一个工厂函数,通过类型鉴别符和Partial<T>传入的属性子集创建子类型实例,但原代码存在两个问题:

  • 调用时无法正确推断返回类型(比如传入'A'应该返回TypeA,但实际是TypeA | TypeB)
  • 传入不符合对应类型的属性时不会触发类型错误(比如给'B'传aProp不会报错)

原代码示例:

type TypeA = {
  discriminator: 'A';
  aProp: string;
};

type TypeB = {
  discriminator: 'B';
  bProp: string;
};

type TypeX = TypeA | TypeB; // 联合类型
type TypeDiscriminator = TypeX['discriminator']; // "A" | "B"

function createInstance(discriminator: TypeDiscriminator, partial: Partial<TypeX>) {
  switch (discriminator) {
    case 'A':
      return { ...partial, discriminator: 'A' } as TypeA;
    case 'B':
      return { ...partial, discriminator: 'B' } as TypeB;
    default:
      throw Error();
  }
}

// 问题:此处应推断为TypeA,但实际是TypeA | TypeB
export const a = createInstance('A', { aProp: 'foobar' });

// 此代码可运行但无效(aProp不存在于TypeB中,应触发错误)
export const b = createInstance('B', { aProp: 'foobar' }) as TypeB;

解决方案(不修改参数列表结构)

核心是通过泛型+映射类型把鉴别符和对应子类型绑定,让TS能根据传入的鉴别符自动约束partial参数的类型,并正确推断返回值。

修改后的完整代码:

type TypeA = {
  discriminator: 'A';
  aProp: string;
};

type TypeB = {
  discriminator: 'B';
  bProp: string;
};

type TypeX = TypeA | TypeB;
type TypeDiscriminator = TypeX['discriminator'];

// 新增映射类型:把鉴别符映射到对应的子类型
type TypeMap = {
  'A': TypeA;
  'B': TypeB;
};

// 改为泛型函数,关联鉴别符和对应类型
function createInstance<K extends TypeDiscriminator>(
  discriminator: K,
  partial: Partial<TypeMap[K]>
): TypeMap[K] {
  switch (discriminator) {
    case 'A':
      return { ...partial, discriminator: 'A' } as TypeMap[K];
    case 'B':
      return { ...partial, discriminator: 'B' } as TypeMap[K];
    default:
      throw new Error(`未知鉴别符: ${discriminator}`);
  }
}

// 现在正确推断为TypeA类型
export const a = createInstance('A', { aProp: 'foobar' });

// 此处会触发TypeScript错误,符合预期(aProp不属于TypeB)
// export const b = createInstance('B', { aProp: 'foobar' });

逻辑说明

  1. TypeMap建立了鉴别符字符串和具体子类型的一一对应关系,让TS能通过鉴别符找到对应的类型
  2. 泛型K被约束为TypeDiscriminator,确保传入的鉴别符只能是'A'或'B'
  3. partial参数被指定为Partial<TypeMap[K]>,意味着必须传入对应子类型的属性子集,不符合的属性会直接报错
  4. 返回值明确为TypeMap[K],TS能根据传入的鉴别符自动推断出具体的子类型,无需手动断言

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:20:03