TypeScript装饰器教程

TypeScript装饰器教程 TypeScript装饰器优雅地扩展你的代码引言什么是装饰器在TypeScript的世界中装饰器Decorators是一种特殊类型的声明它可以被附加到类声明、方法、访问器、属性或参数上。装饰器使用expression的形式其中expression求值后必须是一个函数它会在运行时被调用并接收相应的装饰目标信息作为参数。装饰器的概念最初来自Python和Java等语言而TypeScript在ES7提案的基础上实现了自己的装饰器系统。它们为开发者提供了一种声明式的方法来修改或扩展类及其成员的行为是实现AOP面向切面编程和元编程的强大工具。装饰器的基本语法1. 启用装饰器支持要使用装饰器首先需要在tsconfig.json中启用实验性装饰器功能json{compilerOptions: {experimentalDecorators: true,emitDecoratorMetadata: true}}2. 装饰器工厂装饰器可以通过工厂函数创建这样可以传递自定义参数typescriptfunction log(message: string) {return function(target: any) {console.log(message);console.log(target);};}log(类装饰器)class MyClass {// 类实现}五种装饰器类型详解1. 类装饰器类装饰器应用于类构造函数可以用来观察、修改或替换类定义。typescriptfunction sealed(constructor: Function) {Object.seal(constructor);Object.seal(constructor.prototype);}sealedclass Greeter {greeting: string;constructor(message: string) {this.greeting message;}greet() {return Hello, this.greeting;}}2. 方法装饰器方法装饰器用于类的方法可以拦截方法调用、修改方法行为或添加额外功能。typescriptfunction enumerable(value: boolean) {return function(target: any,propertyKey: string,descriptor: PropertyDescriptor) {descriptor.enumerable value;};}class Person {name: string;constructor(name: string) {this.name name;}enumerable(false)greet() {return Hello, Im ${this.name};}}3. 访问器装饰器访问器装饰器应用于属性的getter或setter。typescriptfunction configurable(value: boolean) {return function(target: any,propertyKey: string,descriptor: PropertyDescriptor) {descriptor.configurable value;};}class Point {private _x: number;private _y: number;constructor(x: number, y: number) {this._x x;this._y y;}configurable(false)get x() {return this._x;}configurable(false)get y() {return this._y;}}4. 属性装饰器属性装饰器用于类的属性声明。typescriptfunction format(formatString: string) {return function(target: any, propertyKey: string) {let value target[propertyKey];const getter () {return value;};const setter (newVal: any) {value newVal;};Object.defineProperty(target, propertyKey, {get: getter,set: setter,enumerable: true,configurable: true});};}class Product {format($0.00)price: number;constructor(price: number) {this.price price;}}5. 参数装饰器参数装饰器应用于构造函数或方法的参数。typescriptfunction validate(min: number, max: number) {return function(target: any,propertyKey: string,parameterIndex: number) {// 存储验证规则供后续使用const existingValidators Reflect.getOwnMetadata(validators, target, propertyKey) || [];existingValidators.push({index: parameterIndex,validator: (value: number) value min value max});Reflect.defineMetadata(validators,existingValidators,target,propertyKey);};}class Calculator {add(validate(0, 100) a: number, validate(0, 100) b: number): number {return a b;}}装饰器执行顺序理解装饰器的执行顺序至关重要1. 参数装饰器然后是方法、访问器或属性装饰器应用到每个实例成员2. 参数装饰器然后是方法、访问器或属性装饰器应用到每个静态成员3. 参数装饰器应用到构造函数4. 类装饰器应用到类typescriptfunction decorator(name: string) {console.log(${name} 求值);return function() {console.log(${name} 调用);};}decorator(类装饰器)class Example {decorator(静态属性)static staticProperty: string;decorator(实例属性)instanceProperty: string;constructor(decorator(构造函数参数) param: string) {}decorator(静态方法)static staticMethod(decorator(静态方法参数) param: string) {}decorator(实例方法)instanceMethod(decorator(实例方法参数) param: string) {}}实际应用场景1. 日志记录typescriptfunction logMethod(target: any,propertyName: string,descriptor: PropertyDescriptor) {const originalMethod descriptor.value;descriptor.value function(...args: any[]) {console.log(调用 ${propertyName}参数: ${JSON.stringify(args)});const result originalMethod.apply(this, args);console.log(结果: ${result});return result;};return descriptor;}class Service {logMethodcalculate(x: number, y: number): number {return x y;}}2. 依赖注入typescriptconst serviceRegistry new Map();function Injectable(token: string) {return function(target: any) {serviceRegistry.set(token, target);return target;};}function Inject(token: string) {return function(target: any, propertyKey: string) {Object.defineProperty(target, propertyKey, {get: () serviceRegistry.get(token),enumerable: true,configurable: true});};}Injectable(logger)class Logger {log(message: string) {console.log([LOG]: ${message});}}class App {Inject(logger)logger: Logger;run() {this.logger.log(应用启动);}}3. 表单验证typescriptinterface ValidationRule {validator: (value: any) boolean;message: string;}function validate(rules: ValidationRule[]) {return function(target: any, propertyKey: string) {const key __${propertyKey}_validations;target[key] rules;const getter function() {return this[_${propertyKey}];};const setter function(newVal: any) {const failedRules rules.filter(rule !rule.validator(newVal));if (failedRules.length 0) {throw new Error(${propertyKey} 验证失败: ${failedRules.map(r r.message).join(, )});}this[_${propertyKey}] newVal;};Object.defineProperty(target, propertyKey, {get: getter,set: setter,enumerable: true,configurable: true});};}class User {validate([{ validator: (v: string) v.length 3, message: 至少3个字符 },{ validator: (v: string) v.length 20, message: 最多20个字符 }])username: string;validate([{ validator: (v: number) v 0, message: 必须是非负数 }])age: number;}装饰器元数据TypeScript的装饰器可以与反射元数据API结合使用提供更强大的元编程能力typescriptimport reflect-metadata;function entity(name: string) {return function(target: Function) {Reflect.defineMetadata(table, name, target);};}function column(options: { type: string, primary?: boolean }) {return function(target: any, propertyKey: string) {const columns Reflect.getMetadata(columns, target) || [];columns.push({ propertyKey, ...options });Reflect.defineMetadata(columns, columns, target);};}Entity(users)class User {column({ type: int, primary: true })id: number;column({ type: varchar(255) })name: string;column({ type: int })age: number;}// 获取元数据const tableName Reflect.getMetadata(table, User);const columns Reflect.getMetadata(columns, User.prototype);最佳实践与注意事项1. 保持装饰器简单装饰器应该专注于单一职责避免过于复杂的逻辑2. 注意执行顺序多个装饰器的执行顺序可能影响最终结果3. 考虑性能影响装饰器在运行时执行可能对性能有轻微影响4. 提供清晰的文档自定义装饰器应该有明确的文档说明其行为5. 避免过度使用装饰器是强大的工具但过度使用会使代码难以理解结语TypeScript装饰器为开发者提供了一种优雅、声明式的方式来扩展和修改代码行为。无论是实现横切关注点如日志、验证、缓存还是构建复杂的框架和库装饰器都是一个强大的工具。随着ECMAScript装饰器提案的演进TypeScript中的装饰器功能也将不断完善为现代Web开发带来更多可能性。掌握装饰器不仅能让你的代码更加整洁和模块化还能让你更好地理解和使用许多流行的TypeScript框架如Angular、NestJS等。现在就开始在你的项目中尝试使用装饰器体验它们带来的便利和强大功能吧