Decorators in TypeScript

Overview

Decorators are an experimental feature of TypeScript (and also a JavaScript stage 2 feature, meaning they will be soon included in standard JS) allowing you to inject specific behaviors to classes, properties, methods, accessors or parameters.

This features allow some kind of meta-programming and dependency injection, called at runtime.

This is mainly used in libs to add specific behaviors to your own code.

For example, TypeORM, an ORM lib, use this feature to give a nice way for users to annotate their models, the dedicated char to use decorator is @ :

@Entity()
export class Person {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  lastname: string;

}

Example

This features needs to be explicitly set as enabled in your tsconfig.json :

    "experimentalDecorators": true

Decorators are just functions, for example, here is a property decorator

class Decorator {

    // Call to the emoji decorator with a string passed as argument
    @emoji("🦍")
    name: string = "";

    constructor(name: string) {
        this.name = name;
    }

}

// The actual decorator code, this is, in fact a decorator factory
// It's a high order function returning the actual decorator
// It's a common and nice way to have access to a larger scope to
// play with the args passed as params (emojo, here)
function emoji(emojo: string) {
    // Return the actual decorator
    return function (target: any, key: string) {

        // get the actual value
        let val = target[key];

        // customize getter
        const getter = () => {
            return val;
        }

        // and setter, to add some nice emojos
        const setter = (next: string) => {
            val = ${emojo} ${next} ${emojo};
        }

        // Apply thoose changes to the actual object property
        Object.defineProperty(target, key, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    }
}

const example = new Decorator("hello");

console.log(example.name);

Even if this example is quite useless, it gives an overview of the possibilities of this feature.

More ressources can be found in the Handbook