mortenl
mortenl

Reputation: 109

Using decorators together with class constructors

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

Answers (1)

jcalz
jcalz

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!

Link to code

Upvotes: 2

Related Questions