DEV Community

Cover image for ๐Ÿง™โ€โ™‚๏ธ JavaScript Decorators Explained โ€“ Like Magic, but Real!
YASH SAWARKAR
YASH SAWARKAR

Posted on

๐Ÿง™โ€โ™‚๏ธ JavaScript Decorators Explained โ€“ Like Magic, but Real!

Decorators are a powerful way to enhance classes and their members (methods or properties) without touching their original code directly. ๐ŸŽฏ
They let you decorate behaviour in a clean and reusable way. Like adding toppings to a pizza ๐Ÿ•

โš ๏ธ Decorators are a Stage-3 proposal in JavaScript. You can use them with TypeScript or Babel.


๐Ÿ” What is a Decorator?

A decorator is a special function you can attach to:

  • A class
  • A class method
  • A class property

It looks like this:

@myDecorator
class MyClass {}
Enter fullscreen mode Exit fullscreen mode

Or for methods:

class MyClass {
  @log
  doSomething() {}
}
Enter fullscreen mode Exit fullscreen mode

โš™๏ธ How Do Decorators Work Behind the Scenes?

Think of a decorator as a function that wraps the original function, property, or class to change its behavior.

Here's what happens behind the scenes for method decorators:

  1. When the class is defined, decorators are executed.
  2. The decorator function receives three arguments:
  • target: The class prototype (for instance methods) or constructor (for static methods)
  • propertyKey: The name of the method/property being decorated
  • descriptor: The property descriptor that describes the method
    1. The decorator can change the method (e.g., add logging) by modifying the descriptor.

Behind-the-scenes sketch:

function log(target, propertyKey, descriptor) {
  // Modify the original function
  const originalMethod = descriptor.value;
  descriptor.value = function (...args) {
    console.log(`Calling ${propertyKey} with`, args);
    return originalMethod.apply(this, args);
  };
  return descriptor;
}
Enter fullscreen mode Exit fullscreen mode

Decorators donโ€™t execute at runtime like normal code โ€” they execute when the class is first defined.


๐Ÿงช Basic Example โ€“ Logging Function Calls

Letโ€™s see a working example:

function log(target, propertyName, descriptor) {
  const original = descriptor.value;

  descriptor.value = function (...args) {
    console.log(`๐Ÿ“ž ${propertyName} called with`, args);
    const result = original.apply(this, args);
    console.log(`โœ… ${propertyName} returned`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @log
  add(a, b) {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(5, 3);
Enter fullscreen mode Exit fullscreen mode

๐Ÿงฐ Real-World Use Cases

1. โœ… Logging and Debugging

Great for tracing code behavior during development.

2. ๐Ÿ” Authorization Guards

function requireAdmin(target, key, descriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args) {
    if (!this.isAdmin) {
      throw new Error("โ›”๏ธ Access denied");
    }
    return original.apply(this, args);
  };
  return descriptor;
}

class Dashboard {
  constructor(isAdmin) {
    this.isAdmin = isAdmin;
  }

  @requireAdmin
  deleteUser() {
    console.log("๐Ÿ—‘๏ธ User deleted");
  }
}

const admin = new Dashboard(true);
admin.deleteUser(); // ๐Ÿ—‘๏ธ User deleted

const guest = new Dashboard(false);
guest.deleteUser(); // โ›”๏ธ Error: Access denied
Enter fullscreen mode Exit fullscreen mode

3. ๐Ÿงน Auto-Binding Methods

function autobind(target, key, descriptor) {
  const original = descriptor.value;
  return {
    configurable: true,
    enumerable: false,
    get() {
      return original.bind(this);
    },
  };
}

class Printer {
  message = "๐Ÿ’จ Hello from Printer!";

  @autobind
  print() {
    console.log(this.message);
  }
}

const p = new Printer();
const printFn = p.print;
printFn(); // ๐Ÿ’จ Hello from Printer!
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ฌ Types of Decorators

JavaScript (and especially TypeScript) supports different types of decorators:

Type Target Purpose
Class decorator Whole class Modify or enhance a class
Method decorator Class method Wrap or replace a method
Property decorator Class property Add metadata or transformations
Parameter decorator Method param Metadata for method parameters

๐Ÿ“Š Setup for TypeScript

Enable decorators in your tsconfig.json:

{
  "compilerOptions": {
    "target": "ES6",
    "experimentalDecorators": true
  }
}
Enter fullscreen mode Exit fullscreen mode

For Babel, use: @babel/plugin-proposal-decorators


๐Ÿง  Summary Table

Feature Target Purpose
@log Method Logging/debugging
@autobind Method Fix this binding
@requireAdmin Method Authorization check

๐Ÿ’ญ Deeper Insight: Why Decorators?

Decorators provide a declarative way to apply cross-cutting concerns like:

  • โœ… Logging
  • ๐Ÿ” Memoization
  • ๐Ÿ” Access control
  • ๐Ÿ’พ Caching
  • โŒš Rate limiting

Rather than adding repetitive boilerplate in every method, you write it once in a decorator and reuse it across your codebase.

This makes your business logic clean, reusable, and easy to read ๐Ÿ’ก


๐Ÿ’ฌ Final Thoughts

Decorators help keep your code clean, elegant, and modular.
They're like invisible helpers that wrap around your logic โ€” magical, but under your control ๐Ÿ’ซ

Use them wisely and your code will thank you!


๐Ÿ“– Resources


Happy Decorating! ๐ŸŒŸ

Top comments (0)