Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are decorators (annotations) compiled in Typescript?

In Angular 2, I can create a component, like this:

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)
  }
}

(source: http://blog.ionic.io/angular-2-series-components/)

This is then compiled into regular ES5.

My question is in 2 parts:

  1. These decorators are specific to Angular. How are they defined?
  2. How do I define my own decorators?
like image 963
superluminary Avatar asked Jan 21 '16 10:01

superluminary


Video Answer


1 Answers

In fact, you should call "annotations" decorators since it's slighty different ;-) They allow to decorate objects. This blog post can give some more hints here: http://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html.

So decorators aren't something specific to Angular. There is a proposal for ES7 and they are also supported by the TypeScript language itself. This can be used in conjonction with the reflect-metadata library (it's contained into the angular2-polyfills.js file) to set and get metadata for elements.

  • Class decorator

    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);
      }
    }
    
  • Function decorator

    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';
      }
    }
    
  • Parameter decorator

    export function MyParameterDecorator(param1) {
      return function(target: any, methodKey: string | symbol,
                      parameterIndex: number) {
        (...)
      };
    }
    
  • Class property decorator

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

So in general a decorator corresponds to a function. If there is no need to return a wrapping one if you don't use parameter. If you want to use parameters for the decorator you need an additional function to get parameters and return the actualy decorator:

// 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');
  };
}

Here is a plunkr corresponding to my samples: https://plnkr.co/edit/0VBthTEuIAsHJjn1WaAX?p=preview.

Here are links that could give you more details with TypeScript:

  • How to implement a typescript decorator?
  • How to avoid hard coded this? in Decorators
  • http://blogs.msdn.com/b/typescript/archive/2015/04/30/announcing-typescript-1-5-beta.aspx

Hope it helps you, Thierry

like image 149
Thierry Templier Avatar answered Oct 07 '22 01:10

Thierry Templier