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

TypeScript仅实现抽象方法的子类可互换问题及解决方法

问题原因

TypeScript采用结构类型系统,类型判断的依据是值的实际结构,而非类的名称(名义)。UserId和OrganizationId的结构完全一致:都继承自抽象类Id,且仅实现了返回string类型的getPrefix方法,没有任何独特的属性或方法差异。因此TypeScript会判定这两个类的实例属于兼容类型,允许互相赋值,自然不会在调用getUser(orgId)时抛出预期的错误。

解决方法

通过给类添加名义类型标识,让TypeScript能够区分不同类的实例,常用的几种实现方式如下:

1. 添加私有属性

给每个子类添加一个唯一的私有属性,私有属性不会被结构类型系统忽略(外部无法访问),以此制造结构差异:

abstract class Id {
  public abstract getPrefix(): string;
}

class UserId extends Id {
  private readonly _userIdBrand: undefined; // 专属私有标识
  public getPrefix(): string {
    return 'user';
  }
}

class OrganizationId extends Id {
  private readonly _organizationIdBrand: undefined; // 专属私有标识
  public getPrefix(): string {
    return 'organization';
  }
}

const getUser = (id: UserId) => {};

const userId = new UserId();
const orgId = new OrganizationId();

getUser(userId);
getUser(orgId); // 现在会抛出预期的类型错误

2. 添加只读公共标识属性

给每个子类添加一个值唯一的readonly公共属性,通过不同的属性值让TypeScript识别结构差异:

abstract class Id {
  public abstract getPrefix(): string;
  public abstract readonly type: string;
}

class UserId extends Id {
  public readonly type = 'user';
  public getPrefix(): string {
    return this.type;
  }
}

class OrganizationId extends Id {
  public readonly type = 'organization';
  public getPrefix(): string {
    return this.type;
  }
}

const getUser = (id: UserId) => {};

const userId = new UserId();
const orgId = new OrganizationId();

getUser(userId);
getUser(orgId); // 类型错误:OrganizationId 无法赋值给 UserId 类型的参数

3. 使用品牌类型(Brand Types)

通过类型别名包装类实例,给不同的ID类型添加专属品牌标记,无需修改类的原始实现:

abstract class Id {
  public abstract getPrefix(): string;
}

// 给不同ID类型添加品牌标记
type UserId = Id & { readonly _brand: 'userId' };
type OrganizationId = Id & { readonly _brand: 'organizationId' };

// 封装实例创建逻辑,通过类型断言添加品牌
class IdImpl extends Id {
  private prefix: string;
  constructor(prefix: string) {
    super();
    this.prefix = prefix;
  }
  public getPrefix(): string {
    return this.prefix;
  }
}

const createUserId = (): UserId => new IdImpl('user') as UserId;
const createOrganizationId = (): OrganizationId => new IdImpl('organization') as OrganizationId;

const getUser = (id: UserId) => {};

const userId = createUserId();
const orgId = createOrganizationId();

getUser(userId);
getUser(orgId); // 类型错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 08:36:04