Decorators are a powerful feature in TypeScript that allow you to add metadata or behavior to classes, methods, properties, and other declarations in your code. Decorators use a special syntax of the @
symbol followed by a function name, which is applied to the target declaration. When the decorator is applied, the function is executed with information about the target, and can modify its behavior or properties.
Defining a decorator
To define a decorator, you simply create a function that takes the target as an argument, and returns either the target or a new object that replaces it. Here’s a simple example of a decorator that logs a message when a method is called:
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling method ${key} with arguments:`, ...args);
const result = originalMethod.apply(this, args);
console.log(`Method ${key} returned:`, result);
return result;
};
return descriptor;
}
In this example, the log
function takes three arguments: target
, which is the class or object that the method belongs to, key
, which is the name of the method, and descriptor
, which contains information about the method. The function then replaces the original method with a new function that logs a message before and after calling the original method.
Applying a decorator
To apply a decorator, you simply add the @
symbol followed by the decorator name to the declaration of the target. For example, to apply the log
decorator to a method called myMethod
in a class called MyClass
, you would write:
class MyClass {
@log
myMethod(arg1: string, arg2: number) {
// ...
}
}
When you call myMethod
, the decorator will be applied and the log messages will be printed to the console.
Decorator factories
You can also define decorator factories, which are functions that return a decorator function. Decorator factories allow you to pass arguments to the decorator, which can then be used to modify the behavior of the decorator. Here’s an example of a decorator factory that logs a message with a custom prefix:
function logWithPrefix(prefix: string) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[${prefix}] Calling method ${key} with arguments:`, ...args);
const result = originalMethod.apply(this, args);
console.log(`[${prefix}] Method ${key} returned:`, result);
return result;
};
return descriptor;
};
}
You can use this decorator factory to create a new decorator with a specific prefix, like this:
class MyClass {
@logWithPrefix('DEBUG')
myMethod(arg1: string, arg2: number) {
// ...
}
}
Now when you call myMethod
, the log messages will include the prefix DEBUG
:
[DEBUG] Calling method myMethod with arguments: arg1, 42
[DEBUG] Method myMethod returned: result