TypesScript Decorator 01

Decorator is very powerful feature in Typescript. It can add additional information or steps to support annotating or modifying. It empowers Typescript with AOP(Aspect-oriented-programming), DI (Dependency Injection) or even meta programming. It's widely used in Angular and Nestjs. If you are familiar with Java SpringBoot, you find them very similar. Whereas in Java, Decorator is called Annotation.

To enable decorator feature, we need to enable experimentalDecorators in tsconfig.json first.

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

There are 5 types of decorators including Class Decorators, Method Decorators, Accessor Decorators, Property Decorators, Parameter Decorators. Each decorator has a slightly different group of parameters. If there are more than 1 type of decorators are added, they will be executed in a well defined order:

  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.

For further information of decorators, you can visit this official documentation.

Here is a playground for Typescript decorators.

Dependency Injection

Class Validator

Routing Controller

Add the action to MetadataArgsStorage;

export function Get(route?: string | RegExp, options?: HandlerOptions): Function {
  return function (object: Object, methodName: string) {
    getMetadataArgsStorage().actions.push({
      type: 'get',
      target: object.constructor,
      method: methodName,
      options,
      route,
    });
  };
}
createExecutor(driver: T, options: RoutingControllerOptions = {}): void {
    return  new RoutingControllers(driver, options)
    .initialize()
    .registerInterceptors(interceptorClasses)
    .registerMiddlewares('before', middlewareClasses)
    .registerControllers(controllerClasses)
    .registerMiddlewares('after', middlewareClasses); 
}

Register the actions of controllers to driver;

registerControllers(classes?: Function[]): this {
    const controllers = this.metadataBuilder.buildControllerMetadata(classes);
    controllers.forEach(controller => {
      controller.actions.forEach(actionMetadata => {
        const interceptorFns = this.prepareInterceptors([
          ...this.interceptors,
          ...actionMetadata.controllerMetadata.interceptors,
          ...actionMetadata.interceptors,
        ]);
        this.driver.registerAction(actionMetadata, (action: Action) => {
          return this.executeAction(actionMetadata, action, interceptorFns);
        });
      });
    });
    this.driver.registerRoutes();
    return this;
  }
registerAction(actionMetadata: ActionMetadata, executeCallback: (options: Action) => any): void {
  this.express[actionMetadata.type.toLowerCase()](
      ...[route, routeGuard, ...beforeMiddlewares, ...defaultMiddlewares, routeHandler, ...afterMiddlewares]
    );
}

Reflect-metadata