如何为TypeScript抽象类泛型参数条件扩展DTO与DomainEntity?
问题描述
现有一个包含三个类型参数Model、DTO和DomainEntity的抽象类:
export abstract class AbstractBaseDataMapper<Model, DTO extends BaseDTO, DomainEntity extends BaseDomainEntity> { public abstract toDomain(t: Model | Model[] | null): Promise<DomainEntity | DomainEntity[] | null>; public abstract toPersistence(t: DomainEntity): Promise<any>; public abstract toDTO(t: DomainEntity | DomainEntity[] | null): Promise<DTO | DTO[] | null>; public dateToString<T extends Date | null>(date: T): StringOrUndefined<T> { throw "unimplemented"; } }
需求是:仅让那些真正相关(比如包含数据库必要字段)的DTO和DomainEntity继承对应的基类。因此需要对第2和第3个类型参数做条件类型处理,但之前尝试的写法无法正常工作:
type A<B> = B & BaseDomainEntity; type DomainEntityOrExtended<T extends BaseDomainEntity> = T extends BaseDomainEntity ? A<T> : T; type Asdf<T extends BaseDomainEntity> = DomainEntityOrExtended<T>; abstract class AbstractBaseDataMapper<Model, DTO extends BaseDTO, DomainEntity extends BaseDomainEntity = Asdf<DomainEntity>> {}
解决方案
核心问题在于你试图通过默认泛型参数强制类型扩展,造成了循环引用,TypeScript无法解析。正确思路是让泛型参数可选,通过条件类型判断传入类型是否需要关联基类。
调整泛型约束与条件类型
将DTO和DomainEntity设为可选泛型,通过条件类型自动适配基类或自定义类型:
// 条件类型:若传入类型兼容基类则用自身,否则 fallback 到基类 type ConditionalBaseDTO<T> = T extends BaseDTO ? T : BaseDTO; type ConditionalBaseDomainEntity<T> = T extends BaseDomainEntity ? T : BaseDomainEntity; abstract class AbstractBaseDataMapper< Model, DTO = BaseDTO, DomainEntity = BaseDomainEntity > { public abstract toDomain( t: Model | Model[] | null ): Promise<ConditionalBaseDomainEntity<DomainEntity> | ConditionalBaseDomainEntity<DomainEntity>[] | null>; public abstract toPersistence( t: ConditionalBaseDomainEntity<DomainEntity> ): Promise<any>; public abstract toDTO( t: ConditionalBaseDomainEntity<DomainEntity> | ConditionalBaseDomainEntity<DomainEntity>[] | null ): Promise<ConditionalBaseDTO<DTO> | ConditionalBaseDTO<DTO>[] | null>; public dateToString<T extends Date | null>(date: T): StringOrUndefined<T> { throw "unimplemented"; } }
更简洁的写法:泛型默认值+私有类型别名
利用私有类型别名封装类型判断逻辑,让代码更清晰:
abstract class AbstractBaseDataMapper< Model, DTO extends BaseDTO | unknown = unknown, DomainEntity extends BaseDomainEntity | unknown = unknown > { // 私有类型别名处理实际类型映射 private type ResolvedDTO = DTO extends BaseDTO ? DTO : BaseDTO; private type ResolvedDomain = DomainEntity extends BaseDomainEntity ? DomainEntity : BaseDomainEntity; public abstract toDomain( t: Model | Model[] | null ): Promise<this["ResolvedDomain"] | this["ResolvedDomain"][] | null>; public abstract toPersistence( t: this["ResolvedDomain"] ): Promise<any>; public abstract toDTO( t: this["ResolvedDomain"] | this["ResolvedDomain"][] | null ): Promise<this["ResolvedDTO"] | this["ResolvedDTO"][] | null>; public dateToString<T extends Date | null>(date: T): StringOrUndefined<T> { throw "unimplemented"; } }
使用示例
- 自定义DTO和DomainEntity的场景:
class CustomDTO extends BaseDTO { customField: string; } class CustomDomainEntity extends BaseDomainEntity { entityField: number; } class CustomDataMapper extends AbstractBaseDataMapper<DBModel, CustomDTO, CustomDomainEntity> { async toDomain(t: DBModel | DBModel[] | null) { return null; } async toPersistence(t: CustomDomainEntity) { return null; } async toDTO(t: CustomDomainEntity | CustomDomainEntity[] | null) { return null; } }
- 使用默认基类的场景:
class SimpleDataMapper extends AbstractBaseDataMapper<DBModel> { async toDomain(t: DBModel | DBModel[] | null) { // 此处DomainEntity类型为BaseDomainEntity return null; } async toPersistence(t: BaseDomainEntity) { return null; } async toDTO(t: BaseDomainEntity | BaseDomainEntity[] | null) { // 此处DTO类型为BaseDTO return null; } }
关键说明
- 避免循环引用:之前的写法中
DomainEntity extends BaseDomainEntity = Asdf<DomainEntity>形成了循环依赖,TypeScript无法解析,改用可选泛型+条件类型可解决此问题。 - 条件类型的作用:通过
T extends BaseXXX判断传入类型是否兼容基类,自动选择使用自定义类型或基类。 - 灵活性:可选泛型参数让用户可自由选择是否传入自定义类型,无需强制继承基类。
内容的提问来源于stack exchange,提问作者Maximilian Haindl
相关产品推荐
相关产品推荐

