TypeScript--装饰器

前言

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

使用

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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'
  • 方法装饰器:接收三个参数,分别是目标类的原型对象,方法的名称,和方法的属性描述符。可以返回一个新的属性描述符或不返回任何值。方法装饰器可以用来修改或替换方法的定义,例如改变方法的可枚举性,或添加额外的逻辑。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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"
  • 属性装饰器:接收两个参数,分别是目标类的原型对象和属性的名称。没有返回值。属性装饰器可以用来修改或替换属性的定义,例如改变属性的可配置性,或定义访问器函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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"
  • 参数装饰器:接收三个参数,分别是目标类的原型对象,方法的名称,和参数在参数列表中的索引。没有返回值。参数装饰器可以用来注入依赖,或检查参数的类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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'