如何让带额外泛型参数的子类正确继承泛型基类?
TypeScript泛型子类继承时泛型推导失效问题解决
问题场景
现有一个泛型基类BaseSerializer,用于序列化/反序列化数据,包含readonly和many两个布尔类型泛型参数:
export class BaseSerializer< R extends boolean = false, M extends boolean = false, > { readonly readonly: R; readonly many: M; constructor(options: { readonly?: R, many?: M } = {}) { // @ts-ignore this.readonly = options?.readonly || false; // @ts-ignore this.many = options?.many || false; } public fromDTO = (data: any): any => { return } public toDTO = (data: any): any => { return } }
当子类无额外泛型参数时,泛型推导正常:
export class DateField< R extends boolean = false, M extends boolean = false, > extends BaseSerializer<R, M> { fromDTO = (data: any) => new Date(data) toDTO = (data: any) => new Date(data).toISOString() } const serializer = new DateField({ many: true }) typeof serializer.many // 类型为true,符合预期
但当子类添加额外泛型参数T后,基类的R/M泛型无法从构造函数参数推导,只能取默认值false:
export class EnumField< T extends any = any, R extends boolean = false, M extends boolean = false, > extends BaseSerializer<R, M>{ fromDTO = (data: any) => data as T toDTO = (data: any) => data as T } type MyEnum = "a" | "b" | "c" const serializer = new EnumField<MyEnum>({ many: true, readonly: true }); // tsafe断言失败:serializer.readonly和many的类型被推导为false assert<Equals<typeof serializer["readonly"], true>>() assert<Equals<typeof serializer["many"], true>>()
需要实现的目标:让EnumField的fromDTO/toDTO方法返回T类型,同时readonly和many字段的类型能从构造参数正确推导。
解决方案
方案1:构造函数泛型重载(推荐)
给EnumField的构造函数添加独立泛型参数,让TypeScript从构造参数自动推导R和M的实际类型,同时保留T的显式指定:
export class EnumField< T extends any = any, R extends boolean = false, M extends boolean = false, > extends BaseSerializer<R, M>{ // 构造函数使用独立泛型RR/MM,覆盖类级别的R/M默认值 constructor<RR extends boolean = R, MM extends boolean = M>( options: { readonly?: RR, many?: MM } = {} ) { super(options); // 类型断言确保赋值符合泛型类型 this.readonly = (options.readonly ?? false) as RR; this.many = (options.many ?? false) as MM; } fromDTO = (data: any): T => data as T toDTO = (data: T): T => data as T } type MyEnum = "a" | "b" | "c" // 显式指定T,TypeScript自动推导RR=true、MM=true const serializer = new EnumField<MyEnum>({ many: true, readonly: true }); // 断言通过 assert<Equals<typeof serializer["readonly"], true>>() assert<Equals<typeof serializer["many"], true>>()
方案2:调整泛型参数顺序
将需要显式指定的T移到泛型参数列表末尾,这样创建实例时可以只显式指定T,让TypeScript自动推导前面的R和M:
export class EnumField< R extends boolean = false, M extends boolean = false, T extends any = any, > extends BaseSerializer<R, M>{ fromDTO = (data: any): T => data as T toDTO = (data: T): T => data as T } type MyEnum = "a" | "b" | "c" // 显式指定R、M、T,或只指定T让TS推导R/M const serializer = new EnumField<true, true, MyEnum>({ many: true, readonly: true }); // 或更简洁的方式:利用类型断言推导 const serializer2 = new EnumField({ many: true, readonly: true }) as EnumField<true, true, MyEnum>;
可选优化:修复基类类型警告
移除基类中的@ts-ignore,通过类型断言确保赋值符合泛型约束:
export class BaseSerializer< R extends boolean = false, M extends boolean = false, > { readonly readonly: R; readonly many: M; constructor(options: { readonly?: R, many?: M } = {}) { this.readonly = (options?.readonly ?? false) as R; this.many = (options?.many ?? false) as M; } public fromDTO = (data: any): any => data public toDTO = (data: any): any => data }
内容的提问来源于stack exchange,提问作者Anton Shuvalov
相关产品推荐
相关产品推荐

