寻求TypeScript中更简洁的Mixin实现方案
寻求TypeScript中更简洁的Mixin实现方案
嘿,太懂你那种被迫继承一个毫无意义的空类的别扭感了!下面给你分享几种更清爽的TypeScript Mixin实现方式,应该能解决你的困扰:
用抽象基类替代空类
把原来的None换成一个语义化的抽象基类,既避免无意义的空类,还能在需要时添加基础约束:abstract class BaseMixin {} type Constructor<T = {}> = new (...args: any[]) => T; function nameMixin<TBase extends Constructor>(Base: TBase) { return class extends Base { #name?: string; name() { return this.#name; } setName(name: string) { this.#name = name; } }; } class Foo extends nameMixin(BaseMixin) { constructor(private value: number) { super(); } } const foo = new Foo(10); foo.setName("john"); console.log(foo.name()); // 输出 "john"优化版原型注入+接口合并
你之前反感Object.assign的耦合问题,那可以把Mixin的类型声明和类绑定,同时保持类型安全:type Constructor<T = {}> = new (...args: any[]) => T; // 先定义Mixin的类型接口 interface NameMixin { name(): string | undefined; setName(name: string): void; } // 封装Mixin的实现逻辑 function createNameMixin<TBase extends Constructor>(Base: TBase) { return class extends Base implements NameMixin { #name?: string; name() { return this.#name; } setName(name: string) { this.#name = name; } }; } // 定义你的业务类 class Foo { constructor(private value: number) {} } // 接口合并让TypeScript识别Foo拥有Mixin的类型 interface Foo extends NameMixin {} // 注入Mixin实现,这里无需空类 Object.assign(Foo.prototype, createNameMixin(class {}).prototype); const foo = new Foo(10); foo.setName("john"); console.log(foo.name());TypeScript 4.9+装饰器方案(最优雅)
如果你的项目已经启用装饰器支持,这种写法最简洁,完全摆脱空类的困扰:type Constructor<T = {}> = new (...args: any[]) => T; // 定义Mixin工厂函数 function NameMixin<TBase extends Constructor>(Base: TBase) { return class extends Base { #name?: string; name() { return this.#name; } setName(name: string) { this.#name = name; } }; } // 直接用装饰器给类应用Mixin @NameMixin class Foo { constructor(private value: number) {} } const foo = new Foo(10); foo.setName("john"); console.log(foo.name());
这几种方案里,我个人首推第一种抽象基类的写法——既保留了你原来继承式Mixin的强类型安全,又去掉了无意义的空类;如果项目允许用装饰器,第三种绝对是最清爽的选择,代码干净直观。
备注:内容来源于stack exchange,提问作者Adam B.
相关产品推荐
相关产品推荐

