TypeScript 装饰器

TypeScript 装饰器是一种特殊语法,允许你在不修改类、方法、属性或参数本身代码的情况下,动态添加元数据或修改其行为。它基于装饰器模式,常用于实现日志记录、验证、依赖注入等功能。装饰器在 TypeScript 中通过 @decoratorName 的语法应用,并需要在 tsconfig.json 中启用 experimentalDecorators 选项。

基本概念
  • 装饰器类型
    • 类装饰器:应用于类构造函数。
    • 方法装饰器:应用于类方法。
    • 属性装饰器:应用于类属性。
    • 参数装饰器:应用于方法参数。
    • 访问器装饰器:应用于 getter/setter。
  • 工作原理:装饰器本质上是一个高阶函数,接收目标对象、属性名等参数,并返回修改后的描述符或新值。例如,方法装饰器的签名是:
    function decorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
      // 修改行为
    }
    
    其中:
    • target:目标类的原型或构造函数。
    • propertyKey:方法或属性的名称(字符串)。
    • descriptor:属性描述符(如可写性、配置性)。
常见用途

装饰器在以下场景中非常实用:

  1. 日志记录:自动记录方法调用和参数。
  2. 验证:检查输入参数或属性值是否有效。
  3. 性能监控:测量方法执行时间。
  4. 依赖注入:在框架(如 Angular)中自动注入服务。
  5. 缓存:存储方法结果以避免重复计算。
代码示例

以下是一个简单的日志装饰器实现,它会在方法调用时输出日志信息。首先,定义一个方法装饰器:

// 定义日志装饰器:记录方法调用和结果
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value; // 保存原始方法
    descriptor.value = function(...args: any[]) {
        console.log(`调用方法: ${propertyKey}, 参数: ${JSON.stringify(args)}`);
        const result = originalMethod.apply(this, args); // 执行原始方法
        console.log(`方法结果: ${result}`);
        return result;
    };
    return descriptor; // 返回修改后的描述符
}

// 应用装饰器到类方法
class Calculator {
    @log
    add(a: number, b: number): number {
        return a + b;
    }
}

// 测试代码
const calc = new Calculator();
calc.add(2, 3); // 控制台输出: 调用方法: add, 参数: [2,3] 和 方法结果: 5

方法装饰器

function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor): void {
  console.log('target', target, `Method ${propertyKey} was called.`, 'descriptor', descriptor);
}

class MyClass1 {
  @LogMethod
  myMethod() {
    console.log('MyMethod was called.');
  }
}
// const myInstance1 = new MyClass1();
// myInstance1.myMethod(); // 输出: Method myMethod was called.

属性装饰器

/**
 * 默认值装饰器工厂函数
 * @param value - 要设置的默认值
 * @returns 返回一个装饰器函数
 */
function DefaultValue(value: any) {
  // 返回装饰器函数
  // 该装饰器用于为类的属性设置默认值
  return function (target: any, propertyKey: string) {
    console.log('target', target, 'propertyKey', propertyKey);
    // 将指定的默认值赋给目标类的属性
    target[propertyKey] = value;
  };
}

class MyClass2 {
  @DefaultValue('default value')
  myProperty: string;
}

// const myInstance2 = new MyClass2();
// console.log(myInstance2.myProperty); // 输出: default value

参数装饰器

/**
 *参数装饰器 
 */

function LogParameter(target: any, propertyKey: string, parameterIndex: number) {
  console.log('target', target, 'propertyKey', propertyKey, 'parameterIndex', parameterIndex);
}

class MyClass3 {
  myMethod(@LogParameter param: string) {
    console.log('param', param);
  }
}

const myInstance3 = new MyClass3();
myInstance3.myMethod('hello'); // 输出: param hello
注意事项
  • 启用装饰器:在 tsconfig.json 中添加 "experimentalDecorators": true
  • 执行顺序:装饰器在类定义时立即执行(而非运行时),多个装饰器时从下到上应用。
  • 浏览器支持:装饰器是 ECMAScript 提案,尚未成为标准,TypeScript 中为实验性功能。建议在编译时使用 Babel 或 TypeScript 编译器处理。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐