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

链式Getter调用限制:避免同一Getter被重复调用

如何实现链式Getter调用的单次调用限制(TypeScript)

要实现每个Getter仅能调用一次的链式调用,核心是通过泛型跟踪已调用的属性名,并在返回类型中动态移除这些已使用的属性。以下是具体实现:

核心思路

  • 为每个装饰器类添加泛型参数,记录已调用过的属性键(初始为never,表示无属性被调用)。
  • 每个Getter的返回类型为当前类的实例,但移除当前Getter属性以及所有已调用过的属性。
  • 通过泛型累积已调用的属性,确保重复调用时类型检查报错。

完整代码实现

class Painter {
  protected textColor?: string;
  protected bgColor?: string;
  protected styles: string[] = [];

  paint() {
    console.log(`Painting: textColor=${this.textColor}, bgColor=${this.bgColor}, styles=${this.styles.join(', ')}`);
  }

  static create() {
    return new TextDecorator<never>();
  }
}

class StyleDecorator<Used extends PropertyKey = never> extends Painter {
  get bold(): Omit<StyleDecorator<Used | 'bold'>, 'bold' | Used> {
    this.styles.push('bold');
    return this as Omit<StyleDecorator<Used | 'bold'>, 'bold' | Used>;
  }

  get underline(): Omit<StyleDecorator<Used | 'underline'>, 'underline' | Used> {
    this.styles.push('underline');
    return this as Omit<StyleDecorator<Used | 'underline'>, 'underline' | Used>;
  }

  get italic(): Omit<StyleDecorator<Used | 'italic'>, 'italic' | Used> {
    this.styles.push('italics');
    return this as Omit<StyleDecorator<Used | 'italic'>, 'italic' | Used>;
  }
}

class BgDecorator<Used extends PropertyKey = never> extends StyleDecorator<Used> {
  get redBg(): Omit<BgDecorator<Used | 'redBg'>, 'redBg' | Used> {
    this.bgColor = "red";
    return this as Omit<BgDecorator<Used | 'redBg'>, 'redBg' | Used>;
  }

  get blueBg(): Omit<BgDecorator<Used | 'blueBg'>, 'blueBg' | Used> {
    this.bgColor = "blue";
    return this as Omit<BgDecorator<Used | 'blueBg'>, 'blueBg' | Used>;
  }
}

class TextDecorator<Used extends PropertyKey = never> extends BgDecorator<Used> {
  get white(): Omit<TextDecorator<Used | 'white'>, 'white' | Used> {
    this.textColor = "white";
    return this as Omit<TextDecorator<Used | 'white'>, 'white' | Used>;
  }

  get black(): Omit<TextDecorator<Used | 'black'>, 'black' | Used> {
    this.textColor = "black";
    return this as Omit<TextDecorator<Used | 'black'>, 'black' | Used>;
  }
}

// 测试案例
Painter.create().white.redBg.bold.underline.paint(); // ✅ 正常执行
Painter.create().white.blueBg.bold.underline.bold.paint(); // ❌ 类型错误:属性'bold'不存在
Painter.create().white.redBg.underline.bold.underline.paint(); // ❌ 类型错误:属性'underline'不存在
Painter.create().white.redBg.italic.underline.italic.underline.paint(); // ❌ 类型错误:属性'italic'和'underline'不存在

关键细节解释

  • 泛型参数Used:用于累积已调用的属性键,每次调用Getter时,将当前属性键加入Used集合。
  • Omit类型工具:返回类型中移除当前Getter属性和所有已调用过的属性,确保重复调用时类型检查不通过。
  • 类型断言:由于TypeScript无法自动推断this的精确类型,使用as断言将this转换为目标类型,确保类型一致性。

这种方案完美适配Getter的链式调用场景,实现了每个属性仅能调用一次的类型约束,同时保留了链式调用的流畅性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:24:51