TS装饰器

174 阅读7分钟

介绍

随着 TypeScript 和 ES6 中类的引入,现在存在某些需要附加功能来支持注释或修改类和类成员的场景。 装饰器提供了一种为类声明和成员添加注释和元编程语法的方法。

命令行: tsc --target ES5 --experimentalDecorators

tsconfig.json

{
    "compilerOptions":{
         "target": "es2016", 
         "experimentalDecorators": true,   
    }
}

装饰器类型

在 TypeScript 中,有4种类型的装饰器:

  1. 类装饰器(Class Decorators):应用于类构造函数,可用于观察、修改或替换类定义。被写在一个类声明之前,紧靠着类声明。
  2. 属性装饰器(Property Decorators):应用于类的属性声明。可以观察和修改类的属性定义。被写在一个属性声明之前。
  3. 方法装饰器(Method Decorators):应用于类的方法声明。可以观察、修改或替换方法定义。并可用来处理类中的方法及其参数。被写在一个方法声明之前。
  4. 参数装饰器(Parameter Decorators):应用于类构造函数或方法的参数。可以对类的成员方法的参数进行操作。

注意:装饰器是一种特殊类型的声明,能够被附加到类声明、方法、属性或参数上,可以修改它们的行为或值。

类装饰器(Class Decorators)

类的装饰器:是一种与类(class)相关的语法,用来注释或修改类和类方法,装饰器本身是一个函数,装饰器通过@来使用.

//注意装饰器的执行时机,不是在 创建实例的时候运行,而是在类创建的时候就会执行。
function testDec(constructor: any) {
  console.log("123");
}

@testDec
class Person {}
//装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
//所以:这里类Person创建完成即会打印log:123

类装饰器的基本语法: 类装饰器是一个函数,可以接受一个参数,即类的构造函数。通常,类装饰器的函数名称以 @ 符号开头,并放在类声明前。示例:

// 修改类的行为,例如在类的构造函数中注入依赖。
// 定义一个装饰器工厂函数
function myDecorator(target: any, propertyKey: any) {
    target.prototype.age = 13
  }
  
  // 使用装饰器工厂函数装饰类的方法
  @myDecorator
  class MyClass {
        name:String
        age:any
        sex:String
        constructor(value:String){
            this.name = value
            this.sex = "man"
        }
  }
  
  const instance = new MyClass('张三');
  console.log(instance.age) //13
 // 定义一个依赖注入的服务
 class MyDependency {
    someValue: string = "Dependency Value"
}
// 定义一个类装饰器
function injectDependency(constructor: any, propertyKey: any) {
    const originalConstructor = constructor
    // 创建一个新构造函数,用于注入依赖
    const newConstructor: any = function (...args: any[]) {
        // 创建依赖实例
        const dependency = new MyDependency();
        // 调用原始构造函数,并传入依赖实例
        return new originalConstructor(dependency, ...args);
    }
    // 设置新构造函数的原型链以继承原始构造函数的原型
    newConstructor.prototype = originalConstructor.prototype;

    return newConstructor;
}

// 使用类装饰器注入依赖
@injectDependency
class MyClass {
  dependency: MyDependency;
  
  constructor(dependency: MyDependency) {
    this.dependency = dependency;
  }

  printDependencyValue() {
    console.log(this.dependency.someValue);
  }
}
let test = { 
    someValue:"测试"
}
// 创建 MyClass 的实例
const myInstance = new MyClass(test);

myInstance.printDependencyValue(); // 输出:Dependency Value

类装饰器的用途: 类装饰器可以用于多种用途,包括但不限于:

  • 添加新属性或方法到类的原型中。
  • 修改类的行为,例如在类的构造函数中注入依赖。
  • 记录或监视类的创建和实例化。
  • 与元数据结合使用,以提供额外的信息或配置。

装饰器工厂函数: 装饰器可以接受参数,使其更具通用性。这些参数允许您根据需要配置装饰器的行为。装饰器工厂函数返回实际的装饰器函数。

类装饰器的返回值: 类装饰器可以返回值,但它们通常不返回任何内容。如果返回一个值,它将替换原始类的构造函数。这样可以用于修改类的构造函数。

属性装饰器(Property Decorators)

属性装饰器(Property Decorators)用于修改类的属性。它允许您在属性的声明上附加元数据、修改属性的访问行为或执行其他操作。

基本语法: 属性装饰器是装饰器函数,可以附加到类的属性声明前。它接受三个参数:

  • 参数1:被装饰的类的原型对象。
  • 参数2:属性的名称。
  • 参数3:属性的属性描述符。

属性装饰器的应用: 属性装饰器可用于执行各种任务,包括但不限于:

  • 修改属性的默认值。
  • 添加元数据,以便其他代码能够识别属性的附加信息。
  • 在属性的 getset 方法中添加逻辑。
  • 执行验证或其他业务逻辑。
//利用属性装饰器添加元数据到属性

import 'reflect-metadata'; // 确保引入 reflect-metadata 库

// 自定义属性装饰器,用于添加元数据
function addMetadata(target: any, propertyKey: any) {
  const metadataKey = 'customMetadata'; // 元数据的键
  const metadataValue = '要添加的元数据值'; // 要添加的元数据值
  Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);
}

class MyClass {
  @addMetadata
  myProperty: string;

  constructor(value: string) {
    this.myProperty = value;
  }
}

// 获取属性的元数据
const instance = new MyClass("测试");
const metadataKey = 'customMetadata';
const metadataValue = Reflect.getMetadata(metadataKey, instance, 'myProperty');

console.log(metadataValue); // 输出:SomeValue
//使用属性装饰器修改属性的默认值

function DefaultValue(value: string) {
    return function(target: any, propertyName: any) {
        Object.defineProperty(target, propertyName, {
            value: value,
            writable: true
        });
    }
}
class MyClass {
    @DefaultValue('default')
    public prop!: string;
}
let obj = new MyClass();
console.log(obj.prop); // 输出 "default"

方法装饰器(Method Decorators)

方法装饰器用于装饰类的方法。它允许您在方法的定义和执行过程中附加额外的行为、元数据或自定义逻辑。方法装饰器通常用于修改方法的行为或收集方法的元数据信息。

function methodDecorator(target: any, key: string, descriptor: PropertyDescriptor) { 
// 在方法装饰器中执行自定义逻辑 
// target: 装饰器的目标对象(类的原型) 
// key: 被装饰方法的名称 
// descriptor: 方法的属性描述符 
}
  • target: 装饰器的目标对象,通常是类的原型。
  • key: 被装饰方法的名称。
  • descriptor: 方法的属性描述符,其中包含有关方法的信息,例如方法的可配置性、可枚举性等。

方法装饰器可以用于各种用途,包括但不限于以下情况:

  1. 修改方法的行为:您可以在方法装饰器中修改方法的实现,添加前置或后置处理逻辑,或者完全替换方法的实现。
  2. 收集元数据:方法装饰器常用于收集有关方法的元数据,例如参数类型、方法参数的验证规则、访问控制等。
  3. 日志记录和性能分析:您可以使用方法装饰器来记录方法的执行,以进行调试或性能分析。
  4. 权限控制:方法装饰器可以用于实现权限控制逻辑,以确定是否允许执行方法。
//使用方法装饰器修改方法的行为
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    let originalMethod = descriptor.value; // 保存原来的方法
    // 重写方法
    descriptor.value = function(...args: any[]) {
        console.log(`调用了 ${propertyKey} 方法,参数为:`, args.join(", "));
        //将参数加2
        let temple = args.map(item => item + 2)
        let result = originalMethod.apply(this, temple)
        console.log(`${propertyKey} 方法的结果是:`, result);
        return result;
    }
    return descriptor;
}
class MyTestClass {
    @LogMethod
    sum(a: number, b: number) {
        return a + b;
    }
}
let obj = new MyTestClass();
console.log(obj.sum(1, 2));  // 7

参数装饰器(Parameter Decorators)

参数装饰器是 TypeScript 中的一种装饰器类型,用于装饰函数或方法的参数。它允许您在函数参数上添加元数据、自定义行为或执行额外的逻辑。参数装饰器通常用于捕获参数的元数据、验证参数值或执行前置或后置处理。 以下是参数装饰器的一般结构:

function parameterDecorator(target: any, methodName: string, parameterIndex: number) {
    // 在参数装饰器中执行自定义逻辑
    // target: 装饰器的目标对象(类的原型或构造函数)
    // methodName: 被装饰方法的名称
    // parameterIndex: 被装饰参数的索引
}
  • target: 装饰器的目标对象,通常是类的原型或构造函数。
  • methodName: 被装饰参数所在方法的名称。
  • parameterIndex: 被装饰参数的索引,从 0 开始。