In the world of modern JavaScript and TypeScript, decorators provide a powerful way to modify classes, methods, properties, and parameters. They are essentially functions that can be attached to various parts of your code to add or change their behavior. This tutorial will introduce you to decorators in TypeScript, focusing on how they can be used to enhance classes and methods.
Decorators are a stage 3 proposal for JavaScript and have been supported by TypeScript since version 1.5. They allow you to annotate and modify classes and class members (methods, properties, accessors, parameters). Decorators are executed when the class is defined, not when an instance of the class is created.
A decorator in TypeScript is a function that takes three arguments:
Decorators are applied using the @decoratorName syntax before a class declaration or a class member.
A class decorator is applied to the constructor function of a class. It can be used to modify or replace the 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;
}
}
In this example, the `sealed` decorator is applied to the `Greeter` class. It uses `Object.seal` to prevent any further modifications to the class or its prototype.
### Method Decorators
A method decorator is applied to a method of a class. It can be used to modify or replace the method definition.
#### Example 2: Logging Method Decorator
```typescript
function log(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling \${propertyKey} with`, args);
const result = originalMethod.apply(this, args);
console.log(`\${propertyKey} returned`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
In this example, the `log` decorator is applied to the `add` method of the `Calculator` class. It logs the arguments with which the method is called and the result returned by the method.
### Property Decorators
A property decorator is applied to a property of a class. It can be used to modify or replace the property definition.
#### Example 3: Required Property Decorator
```typescript
function required(target: Object, propertyKey: string | symbol) {
const descriptor = {
get() {
throw new Error(`Property \${String(propertyKey)} is required`);
},
set(value) {
if (value === undefined || value === null) {
throw new Error(`Property \${String(propertyKey)} cannot be undefined or null`);
}
Object.defineProperty(this, propertyKey, { value });
},
};
return descriptor;
}
class User {
@required
name: string;
constructor(name?: string) {
this.name = name!;
}
}
In this example, the `required` decorator is applied to the `name` property of the `User` class. It ensures that the `name` property cannot be undefined or null.
## What's Next?
Now that you have a good understanding of decorators in TypeScript, it's time to dive deeper into more advanced topics such as **Class Decorators**. These will allow you to modify classes in even more powerful ways, enabling features like dependency injection and other design patterns.
Stay tuned for the next section where we explore class decorators in detail!