โœจ Understanding Method Decorators in TypeScript: A Beginner's Guide

Jobin MathewJobin Mathew
4 min read

๐Ÿš€ Introduction

If you're familiar with JavaScript and new to TypeScript, you might have heard about decorators. Decorators are a powerful feature that allows you to add metadata and modify the behavior of your classes and methods. In this guide, we'll focus on method decorators, explaining what they are and how to use them with practical examples. By the end, you'll see how decorators can simplify and enhance your code.

๐Ÿ” What is a Method Decorator?

A method decorator is a special function that you can attach to a method in a class to modify its behavior. It's a way to add extra functionality or logic, such as logging method calls or measuring execution time.

๐ŸŽจ Basic Syntax

In TypeScript, a method decorator is denoted by the @ symbol followed by the decorator name, placed above the method definition.

class ExampleClass {
  @decorator // โฌ…๏ธ This is where we apply the decorator
  method() {
    // Method logic
  }
}

๐Ÿ› ๏ธ Example: Logging Method Calls

Let's create a simple method decorator that logs each time a method is called. This is a great way to understand the basics of how decorators work.

// ๐ŸŽฏ A method decorator to log method calls
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Save the original method for later use
  const originalMethod = descriptor.value;

  // Modify the method descriptor to wrap the original method
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with arguments:`, args); // ๐Ÿ“ Log the method name and arguments
    return originalMethod.apply(this, args); // ๐Ÿ› ๏ธ Call the original method
  };

  // Return the modified descriptor
  return descriptor;
}

Explanation

  • target: The prototype of the class for instance methods, or the constructor for static methods.

  • propertyKey: The name of the method being decorated.

  • descriptor: An object that provides access to the method's metadata, allowing you to modify its behavior.

๐Ÿš— Applying the Decorator

Now, let's apply this log decorator to a method in a class.

class Car {
  @log // โฌ…๏ธ Apply the log decorator to the drive method
  drive(speed: number) {
    console.log(`Driving at ${speed} km/h`);
  }
}

const myCar = new Car();
myCar.drive(100); 
// Logs: "Calling drive with arguments: [100]"
// Logs: "Driving at 100 km/h"

Breakdown

  • The log decorator is applied to the drive method.

  • Each time the drive method is called, the decorator logs the method name and arguments before executing the original method.

๐Ÿ“˜ How Method Decorators Work

What the decorator does is wrap your custom logic around the original method. This means that when the method is called, the decorator can execute its custom logic before or after the original method runs. The original method remains unchanged and intact; the decorator simply adds extra behavior.

Think of it like wrapping a gift ๐ŸŽ. The gift (original method) is still the same, but you can add a nice wrapping (decorator) to make it more special or functional!

๐ŸŒ Real-World Example:

Measuring Execution Time

Let's create another useful decorator that measures how long a method takes to execute.

// โฑ๏ธ A method decorator to measure execution time
function measureTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Save the original method for later use
  const originalMethod = descriptor.value;

  // Modify the method descriptor to wrap the original method
  descriptor.value = function (...args: any[]) {
    const start = performance.now(); // ๐Ÿ•’ Start time
    const result = originalMethod.apply(this, args); // ๐Ÿ› ๏ธ Call the original method
    const end = performance.now(); // ๐Ÿ•’ End time
    console.log(`${propertyKey} executed in ${(end - start).toFixed(2)} ms`); // ๐Ÿ“ Log the execution time
    return result; // Return the result of the original method
  };

  // Return the modified descriptor
  return descriptor;
}

Explanation

  • The measureTime decorator measures the time taken by the method to execute.

  • performance.now() is used to get the current timestamp in milliseconds.

๐Ÿš€ Applying the Decorator

Let's apply the measureTime decorator to a method.

class Rocket {
  @measureTime // โฌ…๏ธ Apply the measureTime decorator to the launch method
  launch(destination: string) {
    console.log(`Launching to ${destination}`);
    // Simulate some delay
    for (let i = 0; i < 1e6; i++) {}
  }
}

const myRocket = new Rocket();
myRocket.launch("Mars"); 
// Log: "Launching to Mars"
// Log: "launch executed in 1.42 ms"

Breakdown

  • The measureTime decorator is applied to the launch method.

  • It measures and logs the execution time of the launch method each time it's called.

๐ŸŽ‰ Conclusion

Method decorators in TypeScript provide a powerful way to enhance your methods with additional functionality. By understanding and using decorators, you can write cleaner, more maintainable code. Try creating your own decorators and see how they can simplify your development process.

Happy coding! โœจ

0
Subscribe to my newsletter

Read articles from Jobin Mathew directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jobin Mathew
Jobin Mathew

Hey there! I'm Jobin Mathew, a passionate software developer with a love for Node.js, AWS, SQL, and NoSQL databases. When I'm not working on exciting projects, you can find me exploring the latest in tech or sharing my coding adventures on my blog. Join me as I decode the world of software development, one line of code at a time!