Decorators are a powerful feature in TypeScript that allow you to modify or enhance the behavior of classes, methods, properties, and parameters. In this tutorial, we will focus on parameter decorators, which provide a way to add metadata or perform actions on function parameters.
Parameter decorators are particularly useful when you need to inject dependencies, validate inputs, or log function calls. They operate at the level of individual parameters within a method or constructor.
A parameter decorator is a function that is called with three arguments:
The syntax for a parameter decorator looks like this:
function myParameterDecorator(target: any, propertyKey: string | symbol, parameterIndex: number) {
// Your implementation here
}
You can apply a parameter decorator by placing it above the parameter you want to decorate in a method or constructor.
## Examples
### Example 1: Logging Parameter Values
Let's create a simple example where we log the value of each parameter when a method is called.
```typescript
function logParameter(target: any, propertyKey: string | symbol, parameterIndex: number) {
const originalMethod = target[propertyKey];
target[propertyKey] = function (...args: any[]) {
console.log(`Parameter \${parameterIndex}:`, args[parameterIndex]);
return originalMethod.apply(this, args);
};
}
class ExampleClass {
myMethod(@logParameter param1: string, @logParameter param2: number) {
console.log('Inside myMethod');
}
}
const example = new ExampleClass();
example.myMethod('Hello', 42);
In this example, the logParameter decorator logs the value of each parameter when myMethod is called. The output will be:
Parameter 0: Hello Parameter 1: 42 Inside myMethod
Parameter decorators can also be used for dependency injection, where you inject dependencies into a class method.
function inject(target: any, propertyKey: string | symbol, parameterIndex: number) {
const originalMethod = target[propertyKey];
target[propertyKey] = function (...args: any[]) {
args[parameterIndex] = new Dependency();
return originalMethod.apply(this, args);
};
}
class Dependency {
doSomething() {
console.log('Dependency is doing something');
}
}
class ConsumerClass {
myMethod(@inject dependency: Dependency) {
dependency.doSomething();
}
}
const consumer = new ConsumerClass();
consumer.myMethod(null);
In this example, the inject decorator replaces the parameter value with a new instance of Dependency. The output will be:
Dependency is doing something
Parameter decorators can also be used for input validation. For instance, you might want to ensure that a parameter meets certain criteria before proceeding.
function validate(target: any, propertyKey: string | symbol, parameterIndex: number) {
const originalMethod = target[propertyKey];
target[propertyKey] = function (...args: any[]) {
if (typeof args[parameterIndex] !== 'number' || args[parameterIndex] <= 0) {
throw new Error('Parameter must be a positive number');
}
return originalMethod.apply(this, args);
};
}
class ValidatorClass {
myMethod(@validate param1: number) {
console.log(`Validated parameter: \${param1}`);
}
}
const validator = new ValidatorClass();
validator.myMethod(5); // Valid
validator.myMethod(-3); // Throws an error
In this example, the validate decorator checks if the parameter is a positive number. If not, it throws an error.
Now that you have learned about parameter decorators, you might want to explore other advanced features of TypeScript such as async programming. Understanding how to handle asynchronous operations and promises in TypeScript will be crucial for building robust applications.
Stay tuned for the next tutorial on "Async Programming in TypeScript" where we will dive into handling asynchronous code with ease.