如何让TypeScript类装饰器调用实例方法而非静态方法?
解决方案:让TypeScript装饰器触发实例方法的RXJS订阅
你的核心问题是当前装饰器直接操作类(target),只能调用静态方法,无法关联到类的实例。以下是两种可行的实现方案,均能让登录事件触发实例的onLogin()方法:
方案一:拦截类构造函数,为每个实例绑定订阅
通过替换类的构造函数,在实例创建时为其单独绑定登录事件订阅,确保调用的是实例方法:
export function LoginAware() { return function (target: any): any { const loginFunctionName = "onLogin"; const className = target.name || target.toString().match(/\w+/g); const originalConstructor = target; // 定义新的构造函数,拦截实例创建过程 function NewConstructor(...args: any[]) { // 创建类实例 const instance = new originalConstructor(...args); const injector = UtilityService.injector; if (!injector) { return instance; } const authService = injector.get(AuthService); const logger = injector.get(NGXLogger); // 获取当前实例对应的DestroyRef,用于销毁时取消订阅 const destroyRef = injector.get(DestroyRef); if (authService && typeof instance[loginFunctionName] === 'function') { const login$ = authService.login$ .pipe( tap(() => instance[loginFunctionName]()), // 调用实例的onLogin方法 takeUntilDestroyed(destroyRef) ) .subscribe(); destroyRef.onDestroy(() => { login$.unsubscribe(); }); } else { logger.warn(`AuthService不可用,或${className}未实现${loginFunctionName}方法`); } return instance; } // 保持类的原型链完整 NewConstructor.prototype = originalConstructor.prototype; return NewConstructor; } }
方案说明
- 每次创建类实例时,都会执行新构造函数,为该实例单独绑定登录事件订阅
- 订阅与实例的
DestroyRef关联,确保实例销毁时自动取消订阅,避免内存泄漏 - 兼容所有类类型,无论是否依赖Angular生命周期钩子
方案二:扩展Angular生命周期钩子(适用于组件/服务)
如果被装饰的类是Angular组件或服务,可以利用ngOnInit生命周期钩子,在实例初始化后绑定订阅:
export function LoginAware() { return function (target: any): void { const loginFunctionName = "onLogin"; const className = target.name || target.toString().match(/\w+/g); // 保存原有的ngOnInit方法(如果存在) const originalNgOnInit = target.prototype.ngOnInit || (() => {}); // 重写ngOnInit方法 target.prototype.ngOnInit = function (...args: any[]) { // 先执行原有的ngOnInit逻辑 originalNgOnInit.apply(this, args); const injector = UtilityService.injector; if (!injector) return; const authService = injector.get(AuthService); const logger = injector.get(NGXLogger); const destroyRef = injector.get(DestroyRef); if (authService && typeof this[loginFunctionName] === 'function') { const login$ = authService.login$ .pipe( tap(() => this[loginFunctionName]()), // this指向当前实例 takeUntilDestroyed(destroyRef) ) .subscribe(); destroyRef.onDestroy(() => login$.unsubscribe()); } else { logger.warn(`AuthService不可用,或${className}未实现${loginFunctionName}方法`); } }; } }
方案说明
- 符合Angular的生命周期实践,仅在实例初始化完成后绑定订阅
- 自动保留原有
ngOnInit逻辑,不会覆盖业务代码 - 仅适用于实现了Angular生命周期的组件/服务类
关键注意事项
- 方法存在性检查:必须验证实例上是否存在
onLogin方法,避免调用不存在的方法引发错误 - 内存泄漏防护:通过
DestroyRef确保订阅随实例销毁而取消,这在Angular环境中是最佳实践 - 移除不必要的
setTimeout:两种方案均无需延迟等待注入器初始化,因为构造函数/ngOnInit执行时注入器已就绪
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

