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)