Reputation: 109
I'm trying to use decorators in a class that gets initialized with a parameter but the value set in the constructor is undefined when the decorator is apply to my function. Please see the below toy example:
function someDecorator() {
return (target: any, _2: any, descriptor: PropertyDescriptor) => {
const func = descriptor.value.bind(target);
descriptor.value = () => {
console.log('Doing decorator stuff!');
func();
}
}
}
class Foo {
public constructor(private thing: string) { }
@someDecorator()
bar() {
console.log(this.thing);
}
}
new Foo('Hello world!').bar();
I would of cause like to have the output:
> Doing decorator stuff!
> Hello world!
but sadly I get the output:
> Doing decorator stuff!
> undefined
Anybody able to point me in the right direction?
Upvotes: 1
Views: 96
Reputation: 327774
The target
you get will be the class's prototype, not any instance. So binding a method to target
will have the effect of losing the actual instance's this
context and instead calling it in the context of the prototype, which generally won't have any of those instance properties.
You have to wait until the class method is actually called to get an appropriate this
. So forget target
and instead bind func
to this
inside your new method. And note that arrow functions won't have their own this
so descriptor.value
should not be an arrow function (without more hoop jumping). So I'd suggest a traditional anonymous function
instead. So, change someDecorator()
's implementation to something like the following:
function someDecorator() {
return (target: any, _2: any, descriptor: PropertyDescriptor) => {
const func = descriptor.value; // don't bind here
descriptor.value = function (...args: any) {
console.log('Doing decorator stuff!');
func.apply(this, args); // bind here (applying args is optional I guess)
}
}
}
That should work now:
new Foo('Hello world!').bar();
// Doing decorator stuff!
// Hello world!
Hope that helps. Good luck!
Upvotes: 2