Search code examples
typescriptdecoratortypescript-decorator

TS - decorator depends on another regardless of their place in class



Could we run a function decorator @A systematically before @B regardless of their place in the class?


class Exemple {

@A()
public method1(): void { ... }

@B()
public method2(): void { ... }

@A()
public method3(): void { ... }

}

Above, I would like @A of method1 and method3 to run before @B() regardless of the place of the method in the class.


Solution

  • I found a trick with ReflectMetadata:

    A decorator

    
    export const A =
      () =>
      (consumer: any, methodName: string, descriptor: PropertyDescriptor) => {
        const existingAMethods =
          Reflect.getOwnMetadata("A_METHODS", consumer.constructor) || []
        const options: {
          consumer,
          methodName,
          descriptor
        }
        existingAMethods.push(options)
        Reflect.defineMetadata(
          "A_METHODS",
          existingAMethods,
          consumer.constructor
        )
      }
    

    B decorator

    
    export const B =
      () =>
      (consumer: any, methodName: string, descriptor: PropertyDescriptor) => {
        const existingBMethods =
          Reflect.getOwnMetadata("B_METHODS", consumer.constructor) || []
        const options: {
          consumer,
          methodName,
          descriptor
        }
        existingBMethods.push(options)
        Reflect.defineMetadata(
          "B_METHODS",
          existingBMethods,
          consumer.constructor
        )
      }
    

    and a last one "C"

    export const C = () =>
      (constructor: Function): void => {
        const options =
          Reflect.getOwnMetadata("A_METHODS", constructor) || []
        // DO some actions relatecd to A
        const options =
          Reflect.getOwnMetadata("B_METHODS", constructor) || []
        // DO some actions relatecd to B
      }
    

    usage

    @C()
    class Exemple {
    
      @A()
      public method1(): void { ... }
    
      @B()
      public method2(): void { ... }
    
      @A()
      public method3(): void { ... }
    
    }