Typescript decorators

By | December 27, 2023

The easiest way to understand decorators is to think of them as metadata that you can add to your classes,methods and even getter and setter properties

In fact,you can add far more than just information

You can actually extend these elements with additional behavior allowing you to add that behavior to your code without actually changing your code.

Typescript actually encourages developers to opt in to decorator support via configuration setting

In tsconfig file set the experimentalDecorator and emitDecoratorMetadata settings to “true

{
  "compilerOptions": {
      "target": "exnext",
      "noEmit": true,
      "experimentalDecorators": true
      "emitDecoratorMetadata": true
   },
    "include":["src/**/*"]
}

And then you need to install reflect-metadata library

npm i reflect-metadata --save

This library implements polyfill for another proposed Ecmascript features,which are considered experimental

Method Decorator

Implementing a method decorator as writing a function that looks like this

function authorize(role: string){
  return function authorizeDecorator(target: any, property: string, descriptor: PropertyDescriptor){

    const wrapped = descriptor.value;
  
    descriptor.value = function(){
        if(!currentUser.isAuthenticated()){
            throw new Error("Unauthorized");
        }

        if(!currentUser.isInRole(role)){
          throw Error(`User not  in role ${role}`);
        }
  
        try {
          return wrapped.apply(this, arguments);
        }catch(ex){
          throw ex;
        }
    }
  }
}

let currentUser = {
  roles: ["Viewer", "Editor"],
  isAuthenticated(): boolean {
    return true
  },
  isInRole(role:string): boolean {
   return this.roles.contains(role);
  }
}

interface Person {
  id: number;
}

private persons: Person[] = [];

@authorize("Viewer")
  getPersonById(id: number): Person | null {
    const person = this.persons.find((x) => x.id === id);

    return person;
  }

Class Decorator

function freeze(constructor: Function){
    Object.freeze(constructor);
    Object.freeze(constructor.prototype);
}

function singleton<T extends { new (...args:any[]): {}}>(constructor: T){
 return class Singleton extends constructor{
    static _instance = null;

    constructor(...args){
      super(...args);

      if(Singleton._instance){
        throw Error('Duplicate instance');
      }

      Singleton._instance = this;
    }
  }
}

@freeze
@singleton
class Repository {}

Property Decorator

Property decorators are decorators that are applied to properties of a class like the context property of our repository.

You apply a decorator to property the same way you apply one to a class or a method

function auditable(target: object, key: string | symbol){
  //get the initial value, before the decorator is applied
  let val = target[key];

  //then overwrite the property with a custom getter and setter
  Object.defineProperty(target, key, {
    get:() => val,
    set: (newVal) => {
      console.log(`${key.toString()} changed : `, newVal);
      val = newVal;
    },
    enumerable: true,
    configurable: true
  })
}

interface Person {}

class Repository {
  @auditable
  private persons: Person[] = [];
 }

2 thoughts on “Typescript decorators

  1. Online SEO links shop

    It is rare for me to find something on the cyberspace thats as entertaining and fascinating as what you have got here.
    Your page is sweet, your graphics are outstanding, and whats more, you use source that are relevant to
    what you are talking about. You are definitely one in a million, good
    job!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *