Samoy的小窝


一只默默工作的程序猿


欢迎光临Samoy的小屋

Typescript装饰器简介(下)

在上一篇文章中,我们介绍了装饰器的基本概念,并通过一个简单的例子来展示了装饰器的使用方法。然后,Typescript同时支持装饰器的新语法和旧语法。本文来介绍Typescript的旧语法。 在本文中,所使用的语法均为旧语法。

1.装饰器配置

如果通过旧语法使用装饰器,需要在tsconfig.json中配置compilerOptions.experimentalDecorators为true。

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

2.装饰器的类型

  • 属性装饰器
  • 方法装饰器
  • 参数装饰器
  • 类装饰器

3. 装饰器语法

3.1 属性装饰器

function propertyDecorator(target: Object, propertyKey: string | symbol) {
  // 目标对象
  console.log(target);
  // 目标对象的属性名
  console.log(propertyKey);
}

3.2 方法装饰器

function methodDecorator(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) {
  // 目标对象
  console.log(target);
  // 目标对象的属性名
  console.log(propertyKey);
  // 目标对象的属性描述符
  console.log(descriptor);
}

3.3 参数装饰器

function parameterDecorator(target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) {
  // 目标对象
  console.log(target);
  // 目标对象的属性名
  console.log(propertyKey);
  // 目标对象的参数索引
  console.log(parameterIndex);
}

3.4 类装饰器

function classDecorator(target: Function) {
  // 目标对象
  console.log(target);
}

4.装饰器的使用

/**
 * 定义装饰器命名空间
 */
namespace LegacyDecorators {
  /**
   * 最大值的key
   * @private
   */
  const maxKey = Symbol("maxKey");
  /**
   * 最小值的key
   * @private
   */
  const minKey = Symbol("minKey");

  /**
   * 属性装饰器:使属性变成可观察的
   */
  export function observable(): PropertyDecorator {

    function observe(type: 'setter' | 'getter', value: any) {
      console.log(`【observable】 ${value} is ${type}`)
    }

    return function (target: Object, propertyKey: string | symbol) {
      let temp: any;
      Object.defineProperty(target, propertyKey, {
        get() {
          observe('getter', temp);
          return temp;
        },
        set(value) {
          temp = value;
          observe('setter', temp);
        }
      });
    }
  }

  /**
   * 方法装饰器:打印日志
   */
  export function logger(): MethodDecorator {
    return function (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) {
      const originalMethod = descriptor.value;
      descriptor.value = function (...args: any[]) {
        console.log(`%c【logger】${target.constructor.name}.${propertyKey.toString()} has been invoked.`, "color: #ff00ff");
        originalMethod.apply(this, args);
      }
    }
  }


  /**
   * 方法装饰器:校验参数
   */
  export function validate(): MethodDecorator {
    return function (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) {
      const {min, index: minIndex} = Reflect.get(target, minKey);
      const {max, index: maxIndex} = Reflect.get(target, maxKey);
      const originalMethod = descriptor.value;
      descriptor.value = function (...args: any[]) {
        const minValue = args[minIndex];
        const maxValue = args[maxIndex];
        if (minValue !== undefined && minValue < min) {
          throw new Error(`The min value must be greater than ${min}.`);
        }
        if (maxValue !== undefined && maxValue > max) {
          throw new Error(`The max value must be less than ${max}.`);
        }
        return originalMethod.apply(this, args);
      }
    }
  }

  /**
   * 参数装饰器:最大值,需要配合@validate使用
   * @param max
   */
  export function max(max: number): ParameterDecorator {
    return function (target: Object, propertyKey: string | symbol | undefined, index: number) {
      Reflect.set(target, maxKey, {max, index});
    }
  }

  /**
   * 参数装饰器:最小值,需要配合@validate使用
   * @param min
   */
  export function min(min: number): ParameterDecorator {
    return function (target: Object, propertyKey: string | symbol | undefined, index: number) {
      Reflect.set(target, minKey, {min, index});
    }
  }

  /**
   * 类装饰器:密封类,使其不可拓展
   */
  export function sealed(): ClassDecorator {
    return function <T extends Function>(target: T): T | void {
      Object.seal(target);
    }
  }
}

// 以下通过实例演示装饰器的使用
@LegacyDecorators.sealed() // 这里表示类被密封
class Cat {
  @LegacyDecorators.observable() // 这里使name属性变成了可观察的
  name?: string;

  @LegacyDecorators.logger() // 这里进行日志打印
  @LegacyDecorators.validate() // 这里进行参数校验
  sayHello(/*最大值为10,最小值为1*/@LegacyDecorators.max(10) @LegacyDecorators.min(1) age: number) {
    console.log(`Hello, my name is ${this.name}, I'm ${age} years old.`);
  }
}

// 实例化对象
const cat = new Cat();
// 调用name属性的setter
cat.name = "Tom";
// 调用sayHello方法
cat.sayHello(3);
// 打印Cat是否被密封
console.log(`Cat is sealed: ${Object.isSealed(Cat)}`);

5.装饰器的执行顺序

装饰器的执行顺序是先执行属性装饰器,再执行方法装饰器,再执行参数装饰器,最后执行类装饰器。同一类型装饰器,按照由下到上的顺序执行。

6. 总结

虽然Typescript同时支持装饰器的新旧两种语法,但不能混用,要么使用新语法,要么使用旧语法。而且两种装饰器在功能和类型上也不尽相同。需要根据具体场景来决定使用哪种装饰器。

欢迎在评论区留下您的见解~
最近的文章

使用exiv2读取或者修改图片元数据

0. 前言开发环境为Windows,使用vcpkg安装exiv2库。关于vcpkg的安装,请参考vcpkg官方文档。 如果使用其他环境,请自行通过搜索引擎查找相关资料。1. 安装 exiv2vcpkg install exiv22. CMmakeLists.txt 配置include("C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake") # 此处修改为你的vcpkg安装路径find_package(exiv2 CONFIG REQUIRED)ta...…

C/C++
更早的文章

Typescript装饰器简介(上)

1. 装饰器的作用装饰器是一种特殊的声明,它可以被附加到类、方法或属性上,用来修改类,属性或者方法的行为。2. 装饰器类型 类装饰器 方法装饰器 属性装饰器 Getter/Setter装饰器 accessor装饰器3. 装饰器语法目前TypeScript使用的语法是5.0引入的装饰器语法,Typescript同时支持两种语法,本文介绍新语法。旧语法会在下篇文章中介绍。以下是几种装饰器的定义示例:3.1 类装饰器type ClassDecorator = (value: Funct...…

Web