Understanding TypeScript Decorators

TypeScript decorators provide a powerful way to add functionality to classes, methods, properties, and parameters at runtime. They are often used in frameworks like Angular for dependency injection, logging, validation, and more. This article will explain the different types of decorators in TypeScript and how to use them effectively.

What are TypeScript Decorators?

Decorators in TypeScript are functions that are prefixed with an @ symbol and can be attached to various elements like classes, methods, or properties. They allow you to modify the behavior of the code they are attached to, without altering the code directly.

Types of Decorators

  • Class Decorators: Applied to an entire class.
  • Method Decorators: Applied to a method within a class.
  • Property Decorators: Applied to a property in a class.
  • Parameter Decorators: Applied to the parameters of a method in a class.

How to Enable Decorators in TypeScript

Before you can use decorators, make sure to enable them in your tsconfig.json file by setting "experimentalDecorators" to true.

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Class Decorators

A class decorator is applied to an entire class. It is useful for adding metadata or modifying the behavior of a class.

function Controller(route: string) {
  return function (target: Function) {
    target.prototype.route = route;
  };
}

@Controller('/api/user')
class UserController {
  // Class logic
}

console.log(new UserController().route); // Outputs: '/api/user'

Method Decorators

Method decorators are applied to methods within a class. These decorators can be used to modify or log the behavior of a method.

function Log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyName} with arguments: ${args}`);
    return originalMethod.apply(this, args);
  };
}

class Calculator {
  @Log
  add(a: number, b: number) {
    return a + b;
  }
}

const calculator = new Calculator();
calculator.add(2, 3); // Logs: 'Calling add with arguments: 2,3'

Property Decorators

Property decorators are used to add functionality to properties in a class. They can be useful for validation or to add metadata to a property.

function ReadOnly(target: any, key: string) {
  const descriptor: PropertyDescriptor = {
    writable: false
  };
  return descriptor;
}

class Person {
  @ReadOnly
  name: string = 'John Doe';
}

const person = new Person();
person.name = 'Jane Doe'; // Error: Cannot assign to read only property 'name'

Parameter Decorators

Parameter decorators are used to modify or log information about method parameters.

function LogParameter(target: any, propertyName: string, index: number) {
  const metadataKey = `log_${propertyName}_parameters`;
  
  if (Array.isArray(target[metadataKey])) {
    target[metadataKey].push(index);
  } else {
    target[metadataKey] = [index];
  }
}

class Demo {
  method(@LogParameter param1: string, @LogParameter param2: number) {
    // Method logic
  }
}

Conclusion

Decorators in TypeScript provide a powerful way to enhance the functionality of your code without modifying its structure. By understanding how to use class, method, property, and parameter decorators, you can implement advanced features like logging, validation, and dependency injection. Decorators are a key feature in TypeScript that can greatly improve your development workflow, especially in large applications.