DEV Community

Johan du Toit
Johan du Toit

Posted on

TypeScript: It's time to decorate your code πŸŽ‰

Even though Decorators are still experimental (currently stage-2), let's figure out what they are, how we'd use them and whether it changes the way we think.

🚧 To enable experimental support for decorators, you must enable the experimentalDecorators compiler option in tsconfig.json or in the command line using tsc --experimentalDecorators.

So what is a Decorator? A function that can be applied to a class, method, accessor, property or parameter.

πŸ›‘ Before we continue, know that there are two variations: Decorators and Decorator Factories

// Oh look, a function, nothing special here as it just receives a `target` parameter
function basicDecorator(target) {
  // Decorator magic happens here
}

// A function that returns a function, still nothing special here!
function decoratorFactory(someValue: string) {
  return function(target) {
  // Decorator magic happens here
  }
}

// Usage
@basicDecorator
@decoratorFactory("some value")
class MyClass {
  //The expressions for each decorator are evaluated top-to-bottom.
  //The results are then called as functions from bottom-to-top.
}

Topics

πŸŽ‰ Class Decorators
πŸŽ‰ Method Decorators
πŸŽ‰ Accessor Decorators
πŸŽ‰ Property Decorators
πŸŽ‰ Parameter Decorators

πŸŽ‰ Class Decorators

Class decorators allow you to hook into the constructor of a class and allowing us to perform arbitrary code at runtime (before the class is executed/instantiated).

πŸ’€ You can change the prototype of the class (which is highly discouraged) so tread lightly!

function sealed<T>(target: T) {
  Object.seal(target);
  Object.seal(target.prototype);
}

@sealed
class MyClass {
  // class code goes here
}

Would you rather see it all in action? Use the Class Decorator Playground.

πŸŽ‰ Method Decorators

Method decorators allow us to completely override an existing method or better yet add some logic before it's executed (like deboucing, logging, etc.)

// Method decorator
function myMethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 // Update method descriptor, replace the method, etc.
}

class MyClass {
  @myMethodDecorator
  doAction() {
    // Do your thing here
    console.log("doAction has been called");
  }
}

Would you rather see it all in action? Use the Method Decorator Playground.

πŸŽ‰ Accessor Decorators

Unfortunately not many developers use Accessors enough (hopefully this entices you to get more familiar with them). As with Method decorators, it allows us to modify the descriptor (which includes the getters and setters).

function myAccessorDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Update method descriptor, replace the getter and/or setter, etc.
}

class MyClass {
  @myAccessorDecorator
  get myValue() {
    // internal set logic here
  }
}

Would you rather see it all in action? Use the Accessor Decorator Playground.

πŸ›‘ Hold up! The next two decorators are only really 'useful' when using external libraries like reflect-metadata (which adds polyfills for the experimental metadata API), as they don't receive a property descriptor parameter. Once decorators are fully supported, will the reflect-metadata library proposed for adoption.

πŸŽ‰ Property Decorators

I'm sure you've picked up on the pattern already, simply add the decorator before the property. If you're not going to use an external library, then I'd pass on using a property decorator and instead use an accessor or method decorator.

function myPropertyDecorator(target: any, propertyKey: string) {
  // Use an external library like `reflect-metadata`
}

class MyClass {
  @myPropertyDecorator
  myValue: string;

  constructor(value: string) {
    this.myValue = value;
  }
}

Would you rather see it all in action? Use the Property Decorator Playground.

πŸŽ‰ Parameter Decorators

As before simply add the decorator before your parameter of choice. You will notice that we have a new parameter called parameterIndex, which is pretty self explanatory.

function myParameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
  // Use an external library like `reflect-metadata`
  console.log(...arguments);
}

class MyClass {
  doAction(value: string, @myParameterDecorator anotherValue: number) {
    console.log(`doAction called with: ${value} & ${anotherValue}`);
  }
}

Would you rather see it all in action? Use the Parameter Decorator Playground.

Proof read & edited by my beautiful wife ❀!

Remember to #KeepHavingFun

Top comments (0)