前言

TypeScript 中的装饰器是一种特殊的语法,可以用来修改类、方法、属性或参数的行为。装饰器是一种函数,它接收一个目标对象或一个属性描述符作为参数,并可以返回一个新的对象或属性描述符,或者不返回任何值。装饰器可以用来实现一些常见的编程模式,例如依赖注入、日志、缓存、验证等。

使用

要使用装饰器,需要在 tsconfig.json 文件中设置 experimentalDecorators 选项为 true。装饰器的名称通常以@符号开头,例如@log 或@Injectable。装饰器可以应用在类、类的方法、类的属性或类的参数上,分别称为类装饰器、方法装饰器、属性装饰器或参数装饰器。不同类型的装饰器有不同的参数和返回值,具体如下:

  • 类装饰器:接收一个类的构造函数作为唯一参数,可以返回一个新的构造函数或不返回任何值。类装饰器可以用来修改或替换类的定义,例如添加新的属性或方法,或改变类的继承关系。

    function addProperty(target: any) {
      target.prototype.newProperty = "new property";
    }
    
    @addProperty
    class MyClass {
      oldProperty = "old property";
    }
    
    const myObject = new MyClass();
    console.log(myObject.oldProperty); // 'old property'
    console.log(myObject.newProperty); // 'new property'
    
  • 方法装饰器:接收三个参数,分别是目标类的原型对象,方法的名称,和方法的属性描述符。可以返回一个新的属性描述符或不返回任何值。方法装饰器可以用来修改或替换方法的定义,例如改变方法的可枚举性,或添加额外的逻辑。

    function log(target: any, key: string, descriptor: PropertyDescriptor) {
      const originalMethod = descriptor.value;
      descriptor.value = function (...args: any[]) {
        console.log(`Calling ${key} with arguments: ${JSON.stringify(args)}`);
        const result = originalMethod.apply(this, args);
        console.log(`Result: ${JSON.stringify(result)}`);
        return result;
      };
      return descriptor;
    }
    
    class MyClass {
      @log
      myMethod(arg1: string, arg2: number) {
        return `${arg1}-${arg2}`;
      }
    }
    
    const myObject = new MyClass();
    myObject.myMethod("hello", 42); // Calling myMethod with arguments: ["hello",42]
    // Result: "hello-42"
    
  • 属性装饰器:接收两个参数,分别是目标类的原型对象和属性的名称。没有返回值。属性装饰器可以用来修改或替换属性的定义,例如改变属性的可配置性,或定义访问器函数。

    function log(target: any, key: string, descriptor: PropertyDescriptor) {
      const originalGetter = descriptor.get;
      descriptor.get = function () {
        console.log(`Getting ${key}`);
        const result = originalGetter.call(this);
        console.log(`Result: ${JSON.stringify(result)}`);
        return result;
      };
      return descriptor;
    }
    
    class MyClass {
      private _myProperty: string = "";
    
      @log
      get myProperty() {
        return this._myProperty;
      }
    
      set myProperty(value: string) {
        console.log(`Setting myProperty to ${value}`);
        this._myProperty = value;
      }
    }
    
    const myObject = new MyClass();
    myObject.myProperty = "hello";
    console.log(myObject.myProperty); // Getting myProperty
    // Result: "hello"
    // "hello"
    
  • 参数装饰器:接收三个参数,分别是目标类的原型对象,方法的名称,和参数在参数列表中的索引。没有返回值。参数装饰器可以用来注入依赖,或检查参数的类型。

    function uppercase(target: any, key: string) {
      let value = target[key];
      const getter = function () {
        return value;
      };
      const setter = function (newVal: string) {
        value = newVal.toUpperCase();
      };
      Object.defineProperty(target, key, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true,
      });
    }
    
    class MyClass {
      @uppercase
      myProperty: string = "";
    }
    
    const myObject = new MyClass();
    myObject.myProperty = "hello";
    console.log(myObject.myProperty); // 'HELLO'