Types of decorators in typescript
TypeScript offers several types of decorators that can be applied to different parts of a class declaration to add metadata or modify behavior. These types include:
- Class Decorators:
- Applied directly before a class declaration.
- They receive the class constructor as their argument.
- Can be used to observe, modify, or even replace a class definition.
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
- Method Decorators:
- Applied before a method declaration within a class.
- They receive the target object (either the constructor function for static members or the prototype for instance members), the name of the method, and the property descriptor of the method.
- Can be used to modify the behavior of a method, such as adding logging or error handling.
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} returned: ${JSON.stringify(result)}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number) {
return a + b;
}
}
- Accessor Decorators:
- Applied before a getter or setter declaration.
- Similar to method decorators, they receive the target, property key, and property descriptor.
- Can be used to modify the behavior of property accessors.
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
constructor(x: number) {
this._x = x;
}
@configurable(false)
get x() {
return this._x;
}
}
- Property Decorators:
- Applied before a property declaration within a class.
- They receive the target object and the name of the property.
- Primarily used to add metadata to a property, which can then be read at runtime using libraries like
reflect-metadata.
import "reflect-metadata";
const formatMetadataKey = Symbol("format");
function format(formatString: string) {
return Reflect.metadata(formatMetadataKey, formatString);
}
function getFormat(target: any, propertyKey: string) {
return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}
class Post {
@format("DD/MM/YYYY")
createdAt: Date;
constructor(createdAt: Date) {
this.createdAt = createdAt;
}
}
- Parameter Decorators:
- Applied before a parameter in a class constructor or a method declaration.
- They receive the target, the name of the method (or
undefinedfor constructor parameters), and the index of the parameter in the argument list. - Can be used to add metadata to parameters, often for validation or dependency injection purposes.
import "reflect-metadata";
const requiredMetadataKey = Symbol("required");
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}
class GreeterWithValidation {
greet(@required message: string) {
return "Hello, " + message;
}
}