Как декораторы (аннотации) скомпилированы в Typescript?

В Angular 2 я могу создать компонент, например:

import {Component, Template} from 'angular2/angular2'

@Component({
  selector: 'my-component'
})
@View({
  inline: "<div>Hello my name is {{name}}</div>"
})
export class MyComponent {
  constructor() {
    this.name = 'Max'
  }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

(источник: http://blog.ionic.io/angular-2-series-components/)

Затем он компилируется в обычный ES5.

Мой вопрос состоит из 2-х частей:

  • Эти декораторы относятся к Angular. Как они определяются?
  • Как определить мои собственные декораторы?

Ответ 1

На самом деле, вы должны называть декораторы "аннотаций", поскольку они немного разные;-) Они позволяют украшать объекты. Это сообщение в блоге может содержать несколько советов: http://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html.

Таким образом, декораторы не являются чем-то специфичным для Angular. Существует предложение для ES7, и они также поддерживаются языком TypeScript. Это можно использовать в соединении с библиотекой reflect-metadata (она содержится в файле angular2-polyfills.js) для установки и получения метаданных для элементов.

  • Декодер класса

    export function MyClassDecorator(value: string) {
      return function (target: Function) {
        Reflect.defineMetadata("MyClassDecorator", value, target);
      }
    }
    
    @Component({ ... })
    @MyClassDecorator("my metadata")
    export class AppComponent {
      constructor() {
        let decoratorValue: string
          = Reflect.getMetadata("MyClassDecorator", this.constructor);
      }
    }
    
  • Декодер функций

    export function log(target: Object,
                    propertyKey: string,
                    descriptor: TypedPropertyDescriptor<any>) {
      var originalMethod = descriptor.value;
    
      descriptor.value = function(...args: any[]) {
        console.log("The method args are: " + JSON.stringify(args));
        var result = originalMethod.apply(this, args);
        console.log("The return value is: " + result);
        return result;
      };
    
      return descriptor;
    }
    
    export class AppComponent {
      constructor() { }
    
      @MyMethodDecorator
      getMessage() {
        return 'test';
      }
    }
    
  • Декоратор параметров

    export function MyParameterDecorator(param1) {
      return function(target: any, methodKey: string | symbol,
                      parameterIndex: number) {
        (...)
      };
    }
    
  • Декодер свойств класса

    export function MyPropertyDecorator(target: any,
            propertyKey: string | symbol) {
      (...)
    }
    

Итак, в общем, декоратор соответствует функции. Если нет необходимости возвращать обертку, если вы не используете параметр. Если вы хотите использовать параметры для декоратора, вам нужна дополнительная функция для получения параметров и возврата фактического декоратора:

// In the case of a parameter decorator
// myMethod(@MyDecoratorWithoutParameter someParam) { ... }
export function MyDecoratorWithoutParameter(target: any,
    propertyKey: string | symbol, parameterIndex: number) {
  console.log('decorator called');
}

// myMethod(@MyDecoratorWithParameter('test') someParam) { ... }
export function MyDecoratorWithParameter(param1) {
  // param1 contains 'test'
  return function(target: any, propertyKey: string | symbol,
                  parameterIndex: number) {
    console.log('decorator called');
  };
}

Вот plunkr, соответствующий моим образцам: https://plnkr.co/edit/0VBthTEuIAsHJjn1WaAX?p=preview.

Вот ссылки, которые могут дать вам более подробную информацию с помощью TypeScript:

Надеюсь, это поможет вам, Thierry