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 {}
Or for methods:
class MyClass {
@log
doSomething() {}
}
โ๏ธ 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:
- When the class is defined, decorators are executed.
- 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- 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;
}
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);
๐งฐ 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
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!
๐ฌ 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
}
}
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)