TypeScript深度继承树中封装通用构造逻辑的技术问询
封装TypeScript深度继承树中的重复构造逻辑
嘿,我明白你的需求了——现在你的TypeScript代码里,每个子类都要重复写构造函数来合并自身配置和父类配置,这确实挺冗余的。咱们可以通过抽象基类把通用的合并逻辑抽出来,让子类只需要专注于定义自己的特定配置就行。
先看看你可能现在的代码(模拟重复构造的场景):
class BaseConfig { config: Record<string, string>; constructor() { this.config = { baseOpt: "baseOpt" }; } } class SerialConfig extends BaseConfig { constructor() { super(); this.config = { ...this.config, serialOpt: "serialOpt" }; } } class BarConfig extends SerialConfig { constructor() { super(); this.config = { ...this.config, barOpt: "barOpt" }; } } class PyramidConfig extends BarConfig { constructor() { super(); this.config = { ...this.config, pyramidOpt: "pyramidOpt" }; } } // 预期输出:{"baseOpt":"baseOpt","serialOpt":"serialOpt","barOpt":"barOpt","pyramidOpt":"pyramidOpt"} console.log(new PyramidConfig().config);
这里每个子类的构造函数都在重复做super()+扩展配置的操作,完全可以把这部分逻辑封装到基类里。
重构方案:用抽象基类封装通用逻辑
我们可以创建一个抽象基类,强制子类提供自己的特定配置,然后在基类的构造函数里自动遍历整个继承链,合并所有层级的配置:
// 抽象基类,定义通用的配置合并逻辑 abstract class BaseConfig { config: Record<string, string>; constructor() { let currentProto = Object.getPrototypeOf(this); const allConfigs: Record<string, string>[] = []; // 遍历原型链,收集所有父类和子类的specificConfig while (currentProto instanceof BaseConfig) { if ('specificConfig' in currentProto) { allConfigs.push(currentProto.specificConfig); } currentProto = Object.getPrototypeOf(currentProto); } // 按继承顺序合并配置(基类在前,子类在后,子类可覆盖父类同名配置) this.config = Object.assign({}, ...allConfigs); } // 强制子类必须定义自己的特定配置 protected abstract specificConfig: Record<string, string>; } // 最顶层的基础配置类 class RootConfig extends BaseConfig { protected specificConfig = { baseOpt: "baseOpt" }; } // 子类只需要定义自己的特定配置,不需要写构造函数 class SerialConfig extends RootConfig { protected specificConfig = { serialOpt: "serialOpt" }; } class BarConfig extends SerialConfig { protected specificConfig = { barOpt: "barOpt" }; } class PyramidConfig extends BarConfig { protected specificConfig = { pyramidOpt: "pyramidOpt" }; } // 测试输出 const pyramidConfig = new PyramidConfig(); console.log(pyramidConfig.config); // 输出:{"baseOpt":"baseOpt","serialOpt":"serialOpt","barOpt":"barOpt","pyramidOpt":"pyramidOpt"}
方案优势
- 彻底消除重复代码:所有子类不需要再写构造函数,只需要声明自己的
specificConfig属性即可 - 继承顺序保障:基类会按从顶层到底层的顺序合并配置,子类的配置会覆盖父类的同名配置(如果有需要的话)
- 类型安全:抽象类强制子类必须提供
specificConfig,避免遗漏配置定义
如果你觉得抽象类的方式有点重,也可以用简化版的基类构造函数传参方式,但子类还是需要写一行super()调用:
class BaseConfig { config: Record<string, string>; constructor(specificConfig: Record<string, string> = {}) { const parentConfig = Object.getPrototypeOf(this).config || {}; this.config = { ...parentConfig, ...specificConfig }; } } class SerialConfig extends BaseConfig { constructor() { super({ serialOpt: "serialOpt" }); } } // 后续子类同理...
这个方式也能减少重复,但不如抽象类的方式彻底,看你更倾向哪种风格啦。
内容的提问来源于stack exchange,提问作者Eugenio
相关产品推荐
相关产品推荐

