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
相关产品推荐
相关产品推荐

