TypeScript 装饰器基础

什么是装饰器?

装饰器是 TypeScript 中的一种特殊类型的声明,它能够附加到类、方法、属性或参数上,以修改其行为。装饰器的主要目的是提供一种元编程的能力,使得我们可以在不修改原有代码的情况下,增强或改变类的功能。

装饰器的使用场景包括但不限于:

  • 记录日志
  • 进行权限验证
  • 进行性能监控
  • 进行依赖注入

装饰器的类型

TypeScript 支持四种类型的装饰器:

  1. 类装饰器:用于类的构造函数。
  2. 方法装饰器:用于类的方法。
  3. 属性装饰器:用于类的属性。
  4. 参数装饰器:用于类方法的参数。

装饰器的基本语法

在 TypeScript 中,装饰器是一个函数,接受特定的参数。以下是装饰器的基本语法:

function MyDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 装饰器逻辑
}
  • target:被装饰的对象(类的原型或构造函数)。
  • propertyKey:被装饰的属性名(对于类装饰器,此参数为 undefined)。
  • descriptor:属性描述符(仅适用于方法装饰器)。

类装饰器

定义和使用

类装饰器是一个函数,接受一个参数,即类的构造函数。我们可以在类装饰器中修改类的构造函数或添加新的属性和方法。

function LogClass(target: Function) {
    console.log(`Class ${target.name} is created.`);
}

@LogClass
class User {
    constructor(public name: string) {}
}

const user = new User("Alice");

优点

  • 可以在类被实例化之前执行逻辑。
  • 可以为类添加元数据。

缺点

  • 类装饰器不能返回一个新的类,只能修改现有的类。

注意事项

  • 类装饰器必须在类声明之前应用。

方法装饰器

定义和使用

方法装饰器是一个函数,接受三个参数:目标对象、方法名和方法描述符。我们可以在方法装饰器中修改方法的实现。

function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`Method ${propertyKey} is called with args: ${JSON.stringify(args)}`);
        return originalMethod.apply(this, args);
    };
}

class User {
    constructor(public name: string) {}

    @LogMethod
    greet(greeting: string) {
        return `${greeting}, ${this.name}!`;
    }
}

const user = new User("Alice");
user.greet("Hello");

优点

  • 可以在方法调用前后执行逻辑。
  • 可以修改方法的返回值。

缺点

  • 可能会影响性能,尤其是在频繁调用的方法上。

注意事项

  • 方法装饰器不能用于静态方法。

属性装饰器

定义和使用

属性装饰器是一个函数,接受两个参数:目标对象和属性名。属性装饰器通常用于添加元数据或进行验证。

function LogProperty(target: any, propertyKey: string) {
    let value: any;

    const getter = () => {
        console.log(`Getting value of ${propertyKey}`);
        return value;
    };

    const setter = (newValue: any) => {
        console.log(`Setting value of ${propertyKey} to ${newValue}`);
        value = newValue;
    };

    Object.defineProperty(target, propertyKey, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true,
    });
}

class User {
    @LogProperty
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

const user = new User("Alice");
user.name = "Bob"; // Setting value of name to Bob
console.log(user.name); // Getting value of name

优点

  • 可以在属性访问时执行逻辑。
  • 可以用于数据验证。

缺点

  • 属性装饰器的实现相对复杂。

注意事项

  • 属性装饰器不能用于静态属性。

参数装饰器

定义和使用

参数装饰器是一个函数,接受三个参数:目标对象、方法名和参数索引。参数装饰器通常用于记录参数信息或进行验证。

function LogParameter(target: any, propertyKey: string, parameterIndex: number) {
    console.log(`Parameter in method ${propertyKey} at index ${parameterIndex} is decorated.`);
}

class User {
    greet(@LogParameter greeting: string) {
        return `Hello, ${greeting}!`;
    }
}

const user = new User();
user.greet("Alice");

优点

  • 可以在方法调用时记录参数信息。
  • 可以用于参数验证。

缺点

  • 参数装饰器的使用场景相对较少。

注意事项

  • 参数装饰器不能用于静态方法。

总结

装饰器是 TypeScript 中一个强大的特性,能够帮助我们在不修改原有代码的情况下,增强类的功能。通过类装饰器、方法装饰器、属性装饰器和参数装饰器,我们可以实现多种功能,如日志记录、权限验证和性能监控等。

优点总结

  • 提高代码的可重用性和可维护性。
  • 使得代码更加清晰和易于理解。
  • 提供了元编程的能力。

缺点总结

  • 可能会影响性能,尤其是在频繁调用的方法上。
  • 实现相对复杂,可能导致代码难以理解。

注意事项总结

  • 装饰器的使用需要遵循一定的规则,确保其正确性和有效性。
  • 在使用装饰器时,需考虑其对性能的影响,避免在性能敏感的代码中使用。

通过合理使用装饰器,我们可以使 TypeScript 代码更加优雅和高效。希望本教程能帮助你更好地理解和使用 TypeScript 装饰器。