TypeScript 的装饰器(Decorators)是一个强大的特性,允许你向类声明、方法、属性或参数添加元数据。装饰器本质上是一个特殊的声明,它能够被附加到类声明、方法、访问符、属性或参数上。装饰器使用 @expression
的形式,expression
必须求值为一个函数,该函数会在运行时被调用,被装饰的声明信息作为参数传入。
装饰器类型
- 类装饰器:应用于类声明上。
- 方法装饰器:应用于方法上。
- 访问符装饰器:应用于 getter/setter 上。
- 属性装饰器:应用于属性声明上。
- 参数装饰器:应用于参数上。
使用方法
1. 类装饰器
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;
}
}
在这个例子中,sealed
是一个类装饰器,它接收一个构造函数作为参数,并使用 Object.seal
来防止进一步修改构造函数和它的原型。
2. 方法装饰器
function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyName} with`, args);
let result = originalMethod.apply(this, args);
console.log(`Called ${propertyName}. Result: ${result}`);
return result;
};
}
class Calculator {
@log
add(a: number, b: number) {
return a + b;
}
}
const calc = new Calculator();
calc.add(1, 2); // 会在控制台打印日志
log
是一个方法装饰器,它接收三个参数:目标对象、属性名和一个描述符对象。描述符对象可以包含 value
、get
、set
、writable
、enumerable
和 configurable
属性。在这个例子中,我们修改了 value
属性,用一个新的函数替换了原始方法。
3. 属性装饰器
function autobind(target: any, propertyName: string) {
let originalValue = target[propertyName];
Object.defineProperty(target, propertyName, {
configurable: true,
enumerable: true,
get() {
if (typeof originalValue === 'function') {
return originalValue.bind(target);
}
return originalValue;
},
set(value: any) {
originalValue = value;
}
});
}
class App {
@autobind
hello() {
return 'Hello, world!';
}
}
const app = new App();
const helloFunc = app.hello;
console.log(helloFunc()); // 即使函数是通过属性访问的,也能正确绑定到实例
在这个例子中,autobind
是一个属性装饰器,它接收两个参数:目标对象和属性名。我们使用 Object.defineProperty
来定义一个 getter,这样每次访问 hello
属性时,如果它是一个函数,就会返回一个新绑定的函数。
4. 参数装饰器
参数装饰器只应用于类构造函数的参数上。
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
// 这里你可以根据参数索引做一些事情
}
class MyService {
constructor(@required private readonly id: string) {}
}
请注意,参数装饰器不能修改参数本身,但可以用来执行一些副作用,如验证或记录参数值。
总结
TypeScript 的装饰器提供了一种声明式的方式来修改类、方法、属性或参数的行为。虽然它们很强大,但应该谨慎使用,以避免过度复杂化代码结构。在大型项目或框架中,装饰器通常用于添加日志、缓存、验证等横切关注点。