简体   繁体   中英

Typescript decorator: get class decorator value from method decorator

I have a class decorator (fooAnnotation) and method decorator (barDecorator).

import 'reflect-metadata';

function fooAnnotation(value: string){
  console.log('fooAnnotation init...');
  return (target: any): void => {
    console.log('fooAnnotation called...');
    Reflect.defineMetadata('fooAnnotation', value, target);
  };
};

function barAnnotation(value: string){
  console.log('barAnnotation init...');
  return (target: any, propertyKey: any): void => {
    console.log('barAnnotation called...');
    Reflect.defineMetadata('barAnnotation', value, target);
    ...
    const fooAnnotationValue = Reflect.getMetadata('fooAnnotation', target);

   // :( fooAnnotationValue is undefined!!!
  };
};



@fooAnnotation('fooAnnotationValue')
class Foo{

 @barAnnotation('barAnnotationValue')
 public bar: string;
}

My need is that barAnnotation must use the value of fooAnnotation, but unfortunately typescript evaluates class decorators after method decorators, so inside barAnnotation the fooAnnotationMetadata is not defined yet.

How can a method decorator retrieve the value of class decorator?

I would guess that using the Reflect api may not be the best choice here. Assuming that the values you have on the decorators are hardcoded and will not change, you can simply create a decorator factory that shares the data within the factory class:

import "reflect-metadata";

interface IAnnotationFactoryParams {
  classAnnotationValue: string;
  propertyAnnotationValue: string;
}

interface IAnnotationFactoryDecorators {
  classAnnotation: ClassDecorator;
  propertyAnnotation: PropertyDecorator;
}

export class AnnotationFactory {
  constructor(private params: IAnnotationFactoryParams) {}

  public getDecorators(): IAnnotationFactoryDecorators {
    return {
      classAnnotation: (_target: any): void => {
        // both have access to method and class annotation values
        console.log(this.params.classAnnotationValue);
        console.log(this.params.propertyAnnotationValue);
      },
      propertyAnnotation: (_target: any): void => {
        // both have access to method and class annotation values
        console.log(this.params.classAnnotationValue);
        console.log(this.params.propertyAnnotationValue);
      }
    };
  }
}

const values = {
  classAnnotationValue: "foo",
  propertyAnnotationValue: "bar"
};
const decorators = new AnnotationFactory(values).getDecorators();

@decorators.classAnnotation
export class Foo {
  @decorators.propertyAnnotation
  public bar: string;
}

If the values are subject to change then create class Foo also with a factory that uses the annotation factory to produce a class with different values attached to the decorators.

Because the Class Decorator has been executed at the end of class declaration. So, change your mind, you should store something in the property decorator, and process all in the class decorator.

const innerTempPropsName = '__TempProperties__';

function fooAnnotation(value: string){
  console.log('fooAnnotation init...');
  return (ctor: any): void => {
    console.log('fooAnnotation called...');
    Reflect.defineMetadata('fooAnnotation', value, target);
    const vProps = ctor[innerTempPropsName];
    // now process the vProps...
    delete ctor[innerTempPropsName];
  };
};

function barAnnotation(value: string){
  console.log('barAnnotation init...');
  return (ctorPrototype: any, propertyKey: any): void => {
    console.log('barAnnotation called...');
    const ctor = ctorPrototype.constructor;
    let vProps = ctor[innerTempPropsName];
    if (!vProps) ctor[innerTempPropsName] = vProps = {};
    // store temp properties into vProps
    // vProps[propertyKey] = {value, }
    Reflect.defineMetadata('barAnnotation', value, target);
    ...
  };
};



@fooAnnotation('fooAnnotationValue')
class Foo{

 @barAnnotation('barAnnotationValue')
 public bar: string;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM