使用TC39装饰器重写方法后,如何调用父类的原始方法?
TC39标准装饰器继承场景:调用父类原始方法的解决方案
问题背景
项目中使用TC39标准装饰器实现了After装饰器,用于标记类方法在Jest的afterAll阶段执行。但在继承场景下遇到两个问题:
- 基类和派生类的
shutDown方法都标记@After时,枚举逻辑只会执行其中一个方法 - 派生类中调用
super.shutDown()时,触发的是装饰后的父类方法而非原始方法,且Jest的afterAll仅允许调用一次,无法修改枚举逻辑
解决方案
核心思路是在装饰器中保存原始方法的引用,让派生类可以直接访问并调用父类的原始方法,避免触发装饰后的逻辑。
1. 修改After装饰器,保存原始方法
在装饰器返回的包装函数上新增originalMethod属性,存储未被装饰的原始目标方法:
/** A definition of the target function for a function decorator. */ export type DecoratorTargetFunction<This, Args extends unknown[], Return> = (this: This, ...args: Args) => Return; /** * Decorator function for the After annotation. * * @param target The target method. * @param context The context of the decorator. * * @returns a method decorator. */ export function After<This, Args extends unknown[], Return>( target: DecoratorTargetFunction<This, Args, Return>, context: ClassMethodDecoratorContext<This, DecoratorTargetFunction<This, Args, Return>>, ): DecoratorTargetFunction<This, Args, Return> { const result = function (this: This, ...args: Args): Return { afterAll(() => { target.call(this, ...args); }); return void 0 as Return; }; Object.defineProperty(result, "isAfterAll", { value: true }); // 保存原始方法到装饰后的函数实例上 Object.defineProperty(result, "originalMethod", { value: target }); return result; }
2. 派生类中调用父类原始方法
在派生类的shutDown方法中,直接通过父类原型上的装饰后方法获取原始方法,并用call(this)绑定当前实例执行,避免触发父类的装饰逻辑:
export class TestCoreBasics extends BaseTest { @After public shutDown(): void { // 调用父类的原始shutDown方法 BaseTest.prototype.shutDown.originalMethod.call(this); // 执行子类自身的逻辑 ErrorManager.DEFAULT_ERROR_LISTENER.silent = false; } }
原理说明
- 装饰器在类定义阶段就会执行,父类的
shutDown方法被装饰后,BaseTest.prototype.shutDown指向的是装饰后的包装函数 - 通过在包装函数上挂载
originalMethod属性,我们保留了未被修改的原始方法引用 - 派生类直接调用该原始方法,既不会触发父类的
afterAll注册逻辑,又能复用父类的方法实现,同时满足JestafterAll仅调用一次的限制
内容的提问来源于stack exchange,提问作者Mike Lischke
相关产品推荐
相关产品推荐

