I have a class decorator, and that class decorator changes the class and adds a property inside of it. Then i have a method decorator, that's inside a class with that class decorator, and the method decorator, is trying to access the property in the class created by the other decorator.

// The Class Decorator
export function SomeDecorator(): ClassDecorator {
  return target => {
    target['property'] = { text: 'some text' };

    return target;
  }
}

// The Method Decorator
export function SomeOtherDecorator(): MethodDecorator {
  return (target, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    console.log(target['property'].text);
  }
}

// The Class
@SomeDecorator()
export class SomeClass {
  @SomeOtherDecorator()
  someMethod() {}
}

It'll answer with this in runtime: TypeError: Cannot read property 'text' of undefined

Why?

1 Answers

0
hackape On

Like Titian said, class decorators run after method decorators, that's why your example code console.log(target['property'].text); fails.

Not sure about your actual use case, but if you can defer the access to target['property'], you won't have any problem.

function SomeOtherDecorator(): MethodDecorator {
  return (target, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    // no problem, because `target['property']` is only accessed
    // when the decorated method (not decorator) is called.
    descriptor.value = (...args) => {
      console.log(target['property'].text);
      return fn.apply(target, args);
    };
  }
}

You can even use getter to kind of lazy-run the decorator, should take care of most use cases.

function SomeOtherDecorator(): MethodDecorator {
  return (target, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    const fn = descriptor.value;
    let decoratedFn;
    descriptor.get = () => {
      if (decoratedFn) return decoratedFn;
      // do whatever you want to decorate the method/fn
      // including access to `target['property']`
      decoratedFn = ...
    }