Typescript 引用此上下文完整的修饰类方法
我正在编写一个简短的decorator-helper函数,将类转换为事件侦听器 我的问题是,装饰程序会将装饰的方法注册为传入事件的回调,但装饰的方法不会保留它原来的Typescript 引用此上下文完整的修饰类方法,typescript,decorator,Typescript,Decorator,我正在编写一个简短的decorator-helper函数,将类转换为事件侦听器 我的问题是,装饰程序会将装饰的方法注册为传入事件的回调,但装饰的方法不会保留它原来的此上下文 主要问题在这个场景中,我如何保留装饰方法的这个上下文 实施: export function EventHandler (topicKey: any): ClassDecorator { return function (target: any) { const subscriptions = Ref
此
上下文
主要问题在这个场景中,我如何保留装饰方法的这个上下文
实施:
export function EventHandler (topicKey: any): ClassDecorator {
return function (target: any) {
const subscriptions = Reflect.getMetadata('subscriptions', target.prototype)
const topic = Container.get<DomainTopicInterface>(topicKey)
topic.subscribe(event => {
if (subscriptions.length === 0) {
throw new Error(`Event received for '${target.constructor.name}' but no handlers defined`)
}
subscriptions.forEach((subscription: any) => {
subscription.callback(event) // <---- the this context is undefined
})
})
return target
}
}
export function Subscribe (targetClass: StaticDomainEvent<any>): MethodDecorator {
return function (target: Function, methodName: string, descriptor: TypedPropertyDescriptor<any>) {
let originalMethod = descriptor.value
let subscriptions = Reflect.getMetadata('subscriptions', target)
if (!subscriptions) { Reflect.defineMetadata('subscriptions', subscriptions = [], target) }
subscriptions.push({
methodName,
targetClass,
callback: originalMethod
})
}
}
导出函数EventHandler(topicKey:any):ClassDecorator{
返回函数(目标:任意){
const subscriptions=Reflect.getMetadata('subscriptions',target.prototype)
const topic=Container.get(topicKey)
主题。订阅(事件=>{
if(subscriptions.length==0){
抛出新错误(`Event received for'${target.constructor.name}'但未定义任何处理程序`)
}
订阅。forEach((订阅:任意)=>{
subscription.callback(event)//问题是装饰器无法访问此类实例。它仅在类定义上计算一次,目标是类原型。为了获得类实例,它应该装饰类方法或构造函数(扩展类)然后从它的内部获取这个
这是的一个特例。jobCreated
用作回调,因此它应该绑定到上下文。最短的方法是将其定义为箭头:
@Subscribe(JobCreated)
jobCreated = (events: Observable<JobCreated>) => {
console.log(this) // undefined
}
请注意,订阅发生在super
之后,这允许在需要时将原始类构造函数中的方法绑定到其他上下文
Reflect
元数据API也可以替换为常规属性,特别是符号。感谢您的详细响应。尽管尝试了绑定装饰器解决方案,descriptor.value
在调用bind后返回undefined,因为某些原因两者都不是,但我会尝试类似的方法,谢谢您的时间:)我更新了答案。我希望它能在您的案例中起作用。无论如何,如果您的案例中没有生命周期挂钩,订阅应该在类构造函数中进行,即类应该被扩展。请注意,使用了hasOwnMetadata,即层次结构中的每个类都有自己的订阅
。因为调用回调
fr具有子上下文的子类中的om父类是不可取的(具有相同名称的方法可能会被重写,而此调用类似于super[methodName]()),我们应该仅通过方法名来调用它们。很高兴这有帮助。
@Subscribe(JobCreated)
jobCreated = (events: Observable<JobCreated>) => {
console.log(this) // undefined
}
export function EventHandler (topicKey: any): ClassDecorator {
return function (target: any) {
// run only once per class
if (Reflect.hasOwnMetadata('subscriptions', target.prototype))
return target;
target = class extends (target as { new(...args): any; }) {
constructor(...args) {
super(...args);
const topic = Container.get<DomainTopicInterface>(topicKey)
topic.subscribe(event => {
if (subscriptions.length === 0) {
throw new Error(`Event received for '${target.constructor.name}'`)
}
subscriptions.forEach((subscription: any) => {
this[subscription.methodName](event); // this is available here
})
})
}
} as any;
export function Subscribe (targetClass: StaticDomainEvent<any>): MethodDecorator {
return function (target: any, methodName: string, descriptor: TypedPropertyDescriptor<any>) {
// target is class prototype
let subscriptions = Reflect.getOwnMetadata('subscriptions', target);
subscriptions.push({
methodName,
targetClass
// no `callback` because parent method implementation
// doesn't matter in child classes
})
}
}