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

如何为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;
  }
}
关键说明
  1. 避免循环引用:之前的写法中DomainEntity extends BaseDomainEntity = Asdf<DomainEntity>形成了循环依赖,TypeScript无法解析,改用可选泛型+条件类型可解决此问题。
  2. 条件类型的作用:通过T extends BaseXXX判断传入类型是否兼容基类,自动选择使用自定义类型或基类。
  3. 灵活性:可选泛型参数让用户可自由选择是否传入自定义类型,无需强制继承基类。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 21:30:50