如何在Typescript中编写支持接收抽象类参数的类装饰器
解决方案
前提说明
TypeScript 4.2及以上版本正式支持抽象构造签名的类型定义,你之前遇到的语法报错要么是TS版本低于4.2,要么是错误地将abstract修饰符用在了interface的成员上(TS目前不支持接口内部声明抽象构造签名,仅类型别名支持该写法)。
通用实现方案
通过函数重载分别处理普通类和抽象类的传入场景,同时保留完整的类型提示:
// 普通构造函数类型 type Constructor<T = unknown, TArgs extends any[] = any[]> = new (...args: TArgs) => T; // 抽象构造函数类型,TS 4.2+ 支持 type AbstractConstructor<T = unknown, TArgs extends any[] = any[]> = abstract new (...args: TArgs) => T; // 重载1:接收普通类,返回带新增属性的普通类 export function myDecorator<T extends Constructor>(Target: T): T & Constructor<InstanceType<T> & { prop: string; fn(arg: string): void; }>; // 重载2:接收抽象类,返回带新增属性的抽象类 export function myDecorator<T extends AbstractConstructor>(Target: T): T & AbstractConstructor<InstanceType<T> & { prop: string; fn(arg: string): void; }>; export function myDecorator(Target: AbstractConstructor) { // 若不需要在装饰器内实现传入抽象类的抽象方法,可给返回类加abstract修饰,交给后续子类实现 const MyDecoratedClass = class extends Target { prop!: string; fn(arg: string) { console.log(this.prop); } }; const name = `Decorated${Target.name}`; Object.defineProperty(MyDecoratedClass, 'name', { value: name }); return MyDecoratedClass; }
调整后即可直接传入抽象类使用,无需额外的中间层基类:
abstract class SomeAbstractClass { abstract test(): void; } // 正常使用,无类型报错 class MyImplementedClass extends myDecorator(SomeAbstractClass)<string> { foo: string = ''; // 实现抽象方法 test() { console.log('test'); } }
低版本TS兼容方案
如果无法升级TS到4.2以上,可以通过类型断言绕开类型检查,仅能保证运行正常,会丢失部分抽象类的类型校验:
type Class<T, TArgs extends any[] = any[]> = new(...args: TArgs) => T; export function myDecorator<T extends Class<any>>(Target: T) { // 传入抽象类时用as any绕开构造函数类型校验 const MyDecoratedClass = class extends (Target as any) { prop!: string; fn(arg: string) { console.log(this.prop); } }; const name = `Decorated${Target.name}`; Object.defineProperty(MyDecoratedClass, 'name', { value: name }); return MyDecoratedClass as typeof MyDecoratedClass & T; }
内容的提问来源于stack exchange,提问作者dx_over_dt
相关产品推荐
相关产品推荐

