装饰器是TypeScript中一项强大的元编程特性,被Angular和Vue3等主流框架广泛使用。今天我们将深入探讨这一高级特性。
装饰器基础
装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问器、属性或参数上。装饰器使用@expression
形式,其中expression
必须是一个函数。
类装饰器
类装饰器在类声明之前声明,应用于类构造函数,可以观察、修改或替换类定义。
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
装饰器会阻止在类或其原型上添加或删除属性。
装饰器工厂
如果需要自定义装饰器行为,可以使用装饰器工厂:
function color(value: string) {return function (target: any) {// 用某种方式存储元数据target.prototype.color = value;}
}@color("red")
class Car {// ...
}const myCar = new Car();
console.log((myCar as any).color); // 输出: red
方法装饰器
方法装饰器声明在一个方法的声明之前,可以用于观察、修改或替换方法定义。
function log(target: any, key: string, descriptor: PropertyDescriptor) {const original = descriptor.value;descriptor.value = function(...args: any[]) {console.log(`Calling ${key} with`, args);const result = original.apply(this, args);console.log(`Called ${key}, returned`, result);return result;};return descriptor;
}class Calculator {@logadd(a: number, b: number): number {return a + b;}
}const calc = new Calculator();
calc.add(2, 3); // 日志会记录调用和返回
属性装饰器
function format(formatString: string) {return function (target: any, propertyKey: string): any {let value = target[propertyKey];const getter = () => {return `${formatString} ${value}`;};const setter = (newVal: string) => {value = newVal;};return {get: getter,set: setter,enumerable: true,configurable: true};}
}class Greeter {@format("Hello,")greeting: string;constructor(message: string) {this.greeting = message;}
}const greeter = new Greeter("World");
console.log(greeter.greeting); // 输出: Hello, World
参数装饰器
function validate(target: any, propertyKey: string, parameterIndex: number) {const validations = target.__validations__ || (target.__validations__ = {});const paramValidations = validations[propertyKey] || (validations[propertyKey] = []);paramValidations[parameterIndex] = { type: "number", min: 0, max: 100 };
}class Temperature {setValue(@validate value: number) {console.log(`Temperature set to ${value}`);}
}
混入(Mixins)
混入是一种通过组合简单部分类来构建复杂类的模式:
// 辅助函数
type Constructor<T = {}> = new (...args: any[]) => T;function Timestamped<TBase extends Constructor>(Base: TBase) {return class extends Base {timestamp = Date.now();};
}function Activatable<TBase extends Constructor>(Base: TBase) {return class extends Base {isActivated = false;activate() {this.isActivated = true;}deactivate() {this.isActivated = false;}};
}// 使用混入
class User {name = '';
}const TimestampedActivatableUser = Timestamped(Activatable(User));const user = new TimestampedActivatableUser();
user.name = "John";
user.activate();
console.log(user.name); // John
console.log(user.timestamp); // 当前时间戳
console.log(user.isActivated); // true
实际应用场景
-
日志记录:自动记录方法调用和参数
-
性能监控:测量方法执行时间
-
验证:自动验证方法参数
-
依赖注入:如Angular中的@Injectable
-
ORM映射:如TypeORM中的@Entity
最佳实践
-
装饰器应该保持简单和专注
-
避免在装饰器中引入副作用
-
考虑使用装饰器工厂来提高灵活性
-
为装饰器提供清晰的文档说明
-
注意装饰器的执行顺序:
-
参数装饰器 → 方法/属性装饰器 → 类装饰器
-
从下到上(对于同一类型的多个装饰器)
-
装饰器和混入为TypeScript提供了强大的元编程能力,合理使用可以大幅提高代码的可维护性和可扩展性。