链式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
相关产品推荐
相关产品推荐

