DEV Community

Rajesh Rathore
Rajesh Rathore

Posted on • Updated on

Exploring the Power of TypeScript Decorators: Extending and Modifying Code with Ease

TypeScript Decorators

Decorators in TypeScript are a feature that allows you to modify or annotate classes, methods, properties, or parameters at design time. They provide a way to add metadata or behavior to your code using a declarative approach. Decorators are denoted by the @ symbol followed by the decorator function or expression. Here are some examples of decorators in TypeScript:

Class Decorator:
A class decorator is applied to a class declaration and can be used to modify the class or its constructor behavior. It receives the constructor function of the class as its target.

Class Decorator Parameters:

  • target: The target parameter represents the constructor function of the class being decorated. If the class is decorated, target will be the constructor function of that class. It allows you to access and modify the class constructor or its prototype.

Example:

function myClassDecorator(target: any) {
  // Modify the class behavior or prototype here
  target.prototype.customMethod = function () {
    console.log("This is a custom method added by the decorator.");
  };
}

@myClassDecorator
class MyClass {
  // Class implementation
}

const instance = new MyClass();
instance.customMethod(); // Output: "This is a custom method added by the decorator."
Enter fullscreen mode Exit fullscreen mode

Use Case:
A common use case for class decorators is to add functionality or metadata to classes, like logging, access control, or creating singleton instances.

Method Decorator:
A method decorator is applied to a method within a class and allows you to modify the behavior of that method. It receives either the constructor of the class (if the method is static) or the prototype of the class (if the method is an instance method) as its target.

Method Decorator Parameters:

  • target: The prototype of the class if the method is an instance method, or the constructor function of the class if the method is a static method.
  • propertyKey: The name of the method being decorated.
  • descriptor: A PropertyDescriptor object containing the attributes of the method (e.g., value, writable, enumerable, configurable).

Example:

function myMethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling method ${propertyKey} with arguments: ${args}`);
    const result = originalMethod.apply(this, args);
    return result;
  };
}

class ExampleClass {
  @myMethodDecorator
  sayHello(name: string) {
    console.log(`Hello, ${name}!`);
  }
}

const instance = new ExampleClass();
instance.sayHello("John"); // Output: "Calling method sayHello with arguments: John" followed by "Hello, John!"
Enter fullscreen mode Exit fullscreen mode

Use Case:
Method decorators are useful for implementing cross-cutting concerns like logging, measuring execution time, caching, or authentication checks.

Property Decorator:
A property decorator is applied to a property within a class and allows you to modify the behavior of that property. It receives either the constructor of the class (if the property is static) or the prototype of the class (if the property is an instance property) as its target.

Property Decorator Parameters:

  • target: The prototype of the class if the property is an instance property, or the constructor function of the class if the property is a static property.
  • propertyKey: The name of the property being decorated.

Example:

function myPropertyDecorator(target: any, propertyKey: string) {
  const privateKey = `_${propertyKey}`;

  Object.defineProperty(target, propertyKey, {
    get: function () {
      return this[privateKey];
    },
    set: function (value) {
      this[privateKey] = value.toUpperCase();
    },
    enumerable: true,
    configurable: true,
  });
}

class ExampleClass {
  @myPropertyDecorator
  name: string;
}

const instance = new ExampleClass();
instance.name = "John";
console.log(instance.name); // Output: "JOHN"
Enter fullscreen mode Exit fullscreen mode

Use Case:
Property decorators can be used for validation, formatting, or other transformations on class properties before their values are accessed or set.

Parameter Decorator:
A parameter decorator is applied to a parameter of a method or constructor within a class. It allows you to modify the behavior of that specific parameter.

Parameter Decorator Parameters:

  • target: The prototype of the class if the decorator is applied to an instance method or the constructor function if the decorator is applied to a static method.
  • propertyKey: The name of the method if the decorator is applied to an instance method or undefined if applied to a constructor.
  • parameterIndex: The index of the parameter within the method or constructor's parameter list.

Example:

function myParameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
  console.log(`Parameter ${parameterIndex} of method ${propertyKey} in class ${target.constructor.name} is decorated.`);
}

class ExampleClass {
  sayHello(@myParameterDecorator name: string) {
    console.log(`Hello, ${name}!`);
  }
}

const instance = new ExampleClass();
instance.sayHello("John"); // Output: "Parameter 0 of method sayHello in class ExampleClass is decorated." followed by "Hello, John!"
Enter fullscreen mode Exit fullscreen mode

Use Case:
Parameter decorators can be used for logging, validation, or to provide additional context information for methods.

Real-life Use Case of Decorators in TypeScript:

Suppose you are building an Express.js web application and want to implement an authentication middleware that checks if a user is authorized to access certain routes. You can use decorators to achieve this:

// Define an authentication decorator
function authMiddleware(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (req: any, res: any, ...args: any[]) {
    // Check authentication logic here
    const isAuthenticated = checkIfUserIsAuthenticated(req);

    if (isAuthenticated) {
      return originalMethod.apply(this, [req, res, ...args]);
    } else {
      res.status(401).json({ error: "Unauthorized" });
    }
  };
}

class UserController {
  @authMiddleware
  getUserInfo(req: any, res: any) {
    // This route will only be accessible if the user is authenticated
    const userInfo = getUserInfoFromDatabase(req.userId);
    res.json(userInfo);
  }
}

// Express.js route setup (simplified)
app.get("/user", (req, res) => {
  const userController = new UserController();
  userController.getUserInfo(req, res);
});
Enter fullscreen mode Exit fullscreen mode

In this example, the authMiddleware decorator checks if the user is authenticated before allowing access to the getUserInfo method. This helps keep the authentication logic separate and reusable, making the code more maintainable.

Decorators provide a way to extend and modify the behavior of classes, methods, properties, or parameters in a declarative manner. They can be used for various purposes like logging, validation, dependency injection, and more. TypeScript decorators are powerful tools that enhance code readability, reusability, and maintainability.


๐ŸŒŸ Thank You for Joining the Journey! ๐ŸŒŸ

I hope you found this blog post informative and engaging. Your support means the world to me, and I'm thrilled to have you as part of my community. To stay updated on my latest content.

๐Ÿ“Œ Follow me on Social Media! ๐Ÿ“Œ

๐ŸŒ Visit my Website
๐Ÿ“ข Connect with me on Twitter
๐Ÿ“ท Follow me on Instagram
๐Ÿ“š Connect on LinkedIn
๐Ÿ“Œ Check out my GitHub

๐Ÿ’Œ A Special Message to You! ๐Ÿ’Œ

To all my dedicated readers and fellow tech enthusiasts, I want to express my gratitude for your continuous support. Your engagement, comments, and feedback mean the world to me. Let's keep learning, growing, and sharing our passion for development!

๐Ÿ‘ฅ Let's Stay Connected! ๐Ÿ‘ฅ
If you enjoy my content and want to stay in the loop with my latest posts, please consider following me on my social media platforms. Your support is invaluable.

Thank you for being a part of this amazing journey! ๐Ÿš€


Top comments (0)