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

TypeScript如何根据传入Record的值推断泛型联合类型?

让TypeScript自动推断多态工厂函数的返回联合类型

我写了一个polymorficFactory函数,它会根据传入的discriminator属性值,从给定的类映射中实例化对应的类:

type ClassConstructor<T> = {
    new (...args: any[]): T;
};
type ClassMap<T> = Record<string, ClassConstructor<T>>;

function polymorficFactory<T extends object>(
  classMap: ClassMap<T>,
  discriminator: string,
  input: Record<string, any>,
): T {
  if (!input[discriminator]) throw new Error('Input does not have a discriminator property');

  const discriminatorValue = input[discriminator];
  const constructor = classMap[discriminatorValue];

  return plainToInstance(constructor, input); // class-transformer 工具函数
}

这个函数用来处理请求传来的未知Payload对象:

const MOCK_PAYLOAD = {
  type: 'SOME_DTO',
  someProperty: 1,
  someStr: 'Lorem',
  someNestedProp: {
    someBool: true,
  }
} as Record<string, unknown>; // 来自JSON的Payload,类型为Record<string, unknown/any>,但始终是对象
const dto = polymorficFactory<SomeDto | SomeOtherDto>(
  {
    SOME_DTO: SomeDto,
    SOME_OTHER_DTO: SomeOtherDto,
  },
  'type',
  MOCK_PAYLOAD ,
);

dto; // 悬停时类型为"SomeDto | SomeOtherDto"

如果不显式指定类型参数,TypeScript只会推断类映射中第一个值的类型:

const dto = polymorficFactory(
  {
    SOME_DTO: SomeDto,
    SOME_OTHER_DTO: SomeOtherDto,
  },
  'type',
  MOCK_PAYLOAD ,
);

dto; // 悬停时类型为"SomeDto"

我希望无需显式指定类型参数,TypeScript就能自动从classMap的值中推断出所有类实例的联合类型——比如传入包含A: aDto, B: bDto, C: cDto的映射时,返回值自动推断为aDto | bDto | cDto。


解决方案

修改泛型参数的定义,让TypeScript捕获classMap的精确类型,并从中提取所有实例类型的联合:

import { plainToInstance } from 'class-transformer';

type ClassConstructor<T> = {
    new (...args: any[]): T;
};

function polymorficFactory<CM extends Record<string, ClassConstructor<any>>>(
  classMap: CM,
  discriminator: string,
  input: Record<string, unknown>,
): InstanceType<CM[keyof CM]> {
  if (!input[discriminator]) throw new Error('Input does not have a discriminator property');

  const discriminatorValue = input[discriminator] as keyof CM;
  if (!classMap[discriminatorValue]) {
    throw new Error(`No class found for discriminator value: ${discriminatorValue}`);
  }

  const constructor = classMap[discriminatorValue];
  return plainToInstance(constructor, input);
}

关键改动:

  • 泛型CM直接约束为包含构造函数的对象,让TypeScript保留classMap的精确类型信息,而非统一为单一的ClassConstructor<T>
  • 返回值InstanceType<CM[keyof CM]>:
    • keyof CM获取classMap的所有键类型
    • CM[keyof CM]得到所有构造函数类型的联合
    • InstanceType<>将构造函数类型转换为对应的实例类型联合
  • 新增运行时校验,确保传入的discriminator值存在于classMap中,提升健壮性

使用示例:

class SomeDto {
  type!: string;
  someProperty!: number;
}

class SomeOtherDto {
  type!: string;
  anotherProperty!: string;
}

const MOCK_PAYLOAD = {
  type: 'SOME_DTO',
  someProperty: 1,
} as Record<string, unknown>;

// 无需指定泛型参数
const dto = polymorficFactory(
  {
    SOME_DTO: SomeDto,
    SOME_OTHER_DTO: SomeOtherDto,
  },
  'type',
  MOCK_PAYLOAD,
);

// dto类型自动推断为 SomeDto | SomeOtherDto

这样无论classMap包含多少类,TypeScript都会自动推断出所有实例类型的联合,无需手动指定泛型参数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:05:40