Mastering TypeScript Decorators: A Beginner-to-Advanced Guide
Posted by Nuno Marques on 13 Feb 2025
Introduction
TypeScript decorators are a powerful feature that allows developers to enhance classes, methods, properties, and parameters with reusable metadata and functionality. They are widely used in frameworks like Angular to define components, services, and modules efficiently. If you’ve ever wondered how decorators work, when to use them, and how they can streamline your TypeScript projects, this guide is for you.
Estimated reading time: 8 min
What Are TypeScript Decorators?
Decorators in TypeScript are functions that modify the behavior of classes, methods, properties, or parameters at design time. They are similar to annotations in Java or attributes in C#.
Syntax:
function MyDecorator(target: any) {
console.log("Decorator called on:", target);
}
@MyDecorator
class Example {}
Here, @MyDecorator
is applied to the Example
class, and MyDecorator
logs information about the class.
Enabling Decorators in TypeScript
To use decorators, enable the experimentalDecorators
option in tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Types of Decorators in TypeScript
TypeScript provides different types of decorators:
1. Class Decorators
A class decorator is applied to an entire class and can modify its behavior or add metadata.
function Logger(constructor: Function) {
console.log(`Class ${constructor.name} initialized`);
}
@Logger
class Person {
constructor() {
console.log("Person instance created");
}
}
📌 Use case: Logging class instantiations.
2. Method Decorators
Method decorators allow modification of methods within a class.
function LogMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${methodName} called with args:`, args);
return originalMethod.apply(this, args);
};
}
class Calculator {
@LogMethod
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // Logs method call and arguments
📌 Use case: Debugging and logging function calls.
3. Property Decorators
Property decorators can modify or track properties within a class.
function ReadOnly(target: any, propertyName: string) {
Object.defineProperty(target, propertyName, {
writable: false,
configurable: false
});
}
class User {
@ReadOnly
name: string = "John";
}
const user = new User();
user.name = "Mike"; // Error: Cannot assign to 'name' because it is a read-only property.
📌 Use case: Making class properties immutable.
4. Parameter Decorators
Parameter decorators provide metadata about method parameters.
function LogParameter(target: any, methodName: string, paramIndex: number) {
console.log(`Parameter at index ${paramIndex} in method ${methodName} is being logged.`);
}
class Demo {
method(@LogParameter param: string) {
console.log(param);
}
}
📌 Use case: Validating or tracking function parameters.
5. Accessor Decorators
Accessor decorators modify or log getters and setters.
function LogAccessor(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.get;
descriptor.get = function () {
console.log(`Getter for ${methodName} called`);
return originalMethod?.apply(this);
};
}
class Product {
private _price: number = 100;
@LogAccessor
get price() {
return this._price;
}
}
const p = new Product();
console.log(p.price); // Logs message before returning price
📌 Use case: Monitoring property access in debugging scenarios.
Real-World Use Cases
1. Logging and Debugging
Logging function calls or API requests without modifying the original logic.
2. Validation
Ensuring function parameters meet specific conditions.
3. Dependency Injection
Injecting dependencies in frameworks like Angular.
TypeScript Decorators in Angular
Angular makes heavy use of decorators for defining components, services, and modules.
Component Decorators
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `<h1>Welcome to Angular</h1>`
})
export class AppComponent {}
Service Decorators
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class DataService {
fetchData() {
return ["Item1", "Item2"];
}
}
Module Decorators
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
bootstrap: [AppComponent]
})
export class AppModule {}
Key Takeaways
- Decorators provide metadata and modify the behavior of classes, methods, and properties.
- There are five main types of decorators in TypeScript: class, method, property, parameter, and accessor decorators.
- Decorators are widely used in Angular for defining components, services, and modules.
- Practical use cases include logging, validation, and dependency injection.
Next Steps
- Experiment with creating custom decorators.
- Explore advanced decorators with metadata reflection using
reflect-metadata
. - Learn how decorators interact with dependency injection in Angular.