Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 

Repository files navigation

Decorators

This repo shows samples and structurs of decorators in typescript. Its a mixup of different sources

Basics

decorators are still in TS experimentell, so you need do allow them.

Command Line:

tsc --target ES5 --experimentalDecorators

tsconfig.json

{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}

Annotation vs. Decorators

A decorator corresponds to a function that is called on the class whereas annotations are "only" metadata set on the class using the Reflect Metadata library.

With TypeScript and ES7, @Something is a decorator. In the context of Angular2, decorators like @Component, @Injectable, ... define metadata for the decorated element using the Reflect.defineMetadata method.

Annotations

Annotations was an proposed by the Google AtScript team but annotations are not a standard. It adds automatically some boilerplate code to the class, function or property its attached to.

  • Type Annotations
  • Field Annotations
  • MetaData Annotations

type annotation

var num: number = 123;
function identity(num: number): number {
    return num;
}

metadata annotation

import { ComponentMetadata as Component } from '@angular/core';

@Component({
  selector: 'tabs',
  template: `<div>foobar</div>`
})
export class Tabs {

}

field annoation

?

Decorator

Decorators are a proposed standard for ECMAScript 7 by Yehuda Katz, to annotate and modify classes and properties at design time.Looks the same like AtScripts annotation but we are in charge of what our decorator does to our code.

A decorator is:

  • an expression
  • that evaluates to a function
  • that takes the target, name, and decorator descriptor as arguments
  • and optionally returns a decorator descriptor to install on the target object

Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

Consider a simple class definition:

class Person {
  name() { return `${this.first} ${this.last}` }
}

Evaluating this class results in installing the name function onto Person.prototype, roughly like this:

Object.defineProperty(Person.prototype, 'name', {
  value: specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
});

A decorator precedes the syntax that defines a property:

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

Now, before installing the descriptor onto Person.prototype, the engine first invokes the decorator:

let description = {
  type: 'method',
  initializer: () => specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
};

description = readonly(Person.prototype, 'name', description) || description;
defineDecoratedProperty(Person.prototype, 'name', description);

function defineDecoratedProperty(target, { initializer, enumerable, configurable, writable }) {
  Object.defineProperty(target, { value: initializer(), enumerable, configurable, writable });
}

The has an opportunity to intercede before the relevant defineProperty actually occurs.

Decorator Factories

If we want to customize how a decorator is applied to a declaration, we can write a decorator factory. A Decorator Factory is simply a function that returns the expression that will be called by the decorator at runtime.

We can write a decorator factory in the following fashion:

function color(value: string) { // this is the decorator factory
    return function (target) { // this is the decorator
        // do something with 'target' and 'value'...
    }
}

Decorator Composition

Multiple decorators can be applied to a declaration, as in the following examples:

On a single line:

@f @g x

On multiple lines:

@f
@g
x

Decorator Evaluation

There is a well defined order to how decorators applied to various declarations inside of a class are applied:

  1. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each instance member.
  2. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each static member.
  3. Parameter Decorators are applied for the constructor.
  4. Class Decorators are applied for the class.

Class Decorator

Example use: Using the metadata api to store information on a class.

No parameters:

function ClassDecorator(
    target: Function // The class the decorator is declared on
    ) {
    console.log("ClassDecorator called on: ", target);
}

@ClassDecorator
class ClassDecoratorExample {
}

Output

    ClassDecorator called on:  function ClassDecoratorExample() {
     }

With parameters:

function ClassDecoratorParams(param1: number, param2: string) {
    return function(
        target: Function // The class the decorator is declared on
        ) {
        console.log("ClassDecoratorParams(" + param1 + ", '" + param2 + "') called on: ", target);
    }
}

@ClassDecoratorParams(1, "a")
@ClassDecoratorParams(2, "b")
class ClassDecoratorParamsExample {
}

Output

    ClassDecoratorParams(2, 'b') called on:  function ClassDecoratorParamsExample() {
     }
    ClassDecoratorParams(1, 'a') called on:  function ClassDecoratorParamsExample() {
     }

Property Decorator

Example use: Creating a @serialize("serializedName") decorator and adding the property name the a list of properties to serialize.

function PropertyDecorator(
    target: Object, // The prototype of the class
    propertyKey: string | symbol // The name of the property
    ) {
    console.log("PropertyDecorator called on: ", target, propertyKey);
}

class PropertyDecoratorExample {
    @PropertyDecorator
    name: string;
}

Output

    PropertyDecorator called on:  {} name

Method Decorator

function MethodDecorator(
    target: Object, // The prototype of the class
    propertyKey: string, // The name of the method
    descriptor: TypedPropertyDescriptor<any>
    ) {
    console.log("MethodDecorator called on: ", target, propertyKey, descriptor);
}

class MethodDecoratorExample {
    @MethodDecorator
    method() {
    }
}

Output

    MethodDecorator called on:  { method: [Function] } method { value: [Function],
      writable: true,
      enumerable: true,
      configurable: true }

Restrict to a certain function signature:

function TypeRestrictedMethodDecorator(
    target: Object, // The prototype of the class
    propertyKey: string, // The name of the method
    descriptor: TypedPropertyDescriptor<(num: number) => number>
    ) {
    console.log("TypeRestrictedMethodDecorator called on: ", target, propertyKey, descriptor);
}

class TypeRestrictedMethodDecoratorExample {
    @TypeRestrictedMethodDecorator
    method(num: number): number {
        return 0;
    }
}

Output

    TypeRestrictedMethodDecorator called on:  { method: [Function] } method { value: [Function],
      writable: true,
      enumerable: true,
      configurable: true }

Static Method Decorator

function StaticMethodDecorator(
    target: Function, // the function itself and not the prototype
    propertyKey: string | symbol, // The name of the static method
    descriptor: TypedPropertyDescriptor<any>
    ) {
    console.log("StaticMethodDecorator called on: ", target, propertyKey, descriptor);
}

class StaticMethodDecoratorExample {
    @StaticMethodDecorator
    static staticMethod() {
    }
}

Output

    StaticMethodDecorator called on:  function StaticMethodDecoratorExample() {
      }

Parameter Decorator

function ParameterDecorator(
    target: Function, // The prototype of the class
    propertyKey: string | symbol, // The name of the method
    parameterIndex: number // The index of parameter in the list of the function's parameters
    ) {
    console.log("ParameterDecorator called on: ", target, propertyKey, parameterIndex);
}

class ParameterDecoratorExample {
    method(@ParameterDecorator param1: string, @ParameterDecorator param2: number) {
    }
}

Output

    ParameterDecorator called on:  { method: [Function] } method 1
    ParameterDecorator called on:  { method: [Function] } method 0

figures

sources

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors