TypeScript装饰器详解:从基础到应用

2025/3/8
本文详细介绍了TypeScript中的装饰器,包括基本概念、类型、应用场景,如日志记录、权限控制等,还提到装饰器处于实验阶段及使用注意事项。
TypeScript装饰器概念示意图,TypeScript装饰器类型分类图,TypeScript装饰器应用场景示例代码截图

TypeScript 中的装饰器(Decorators)是一种特殊类型的声明,它可以被附加到类声明、方法、访问器、属性或参数上,以修改类的行为。装饰器使用 @expression 的形式,其中 expression 必须是一个函数,它会在运行时被调用,并以被装饰的声明作为参数。

装饰器的基本概念

装饰器本质上是一个函数,它接收三个参数:

  1. target: 对于类装饰器,target 是类的构造函数;对于方法装饰器,target 是类的原型;对于属性装饰器,target 是类的原型或类的构造函数(取决于装饰的是实例属性还是静态属性)。
  2. key: 被装饰的属性或方法的名称。
  3. descriptor: 对于方法装饰器,descriptor 是属性描述符(PropertyDescriptor);对于属性装饰器,descriptorundefined

装饰器的类型

  1. 类装饰器:应用于类构造函数,可以用来观察、修改或替换类定义。
  2. 方法装饰器:应用于类的方法,可以用来修改方法的行为。
  3. 访问器装饰器:应用于类的访问器(getter/setter),可以用来修改访问器的行为。
  4. 属性装饰器:应用于类的属性,可以用来修改属性的行为。
  5. 参数装饰器:应用于方法的参数,可以用来修改参数的行为。

装饰器的应用场景

  1. 日志记录:通过方法装饰器,可以在方法执行前后自动记录日志。

    function log(target: any, key: string, descriptor: PropertyDescriptor) {
      const originalMethod = descriptor.value;
      descriptor.value = function (...args: any[]) {
        console.log(`Calling ${key} with`, args);
        const result = originalMethod.apply(this, args);
        console.log(`Called ${key}, returned`, result);
        return result;
      };
      return descriptor;
    }
    
    class MyClass {
      @log
      myMethod(arg: string) {
        return `Hello, ${arg}`;
      }
    }
    
    const instance = new MyClass();
    instance.myMethod('World'); // Logs: Calling myMethod with ['World'], Called myMethod, returned Hello, World
    
  2. 权限控制:通过方法装饰器,可以在方法执行前检查用户权限。

    function checkPermission(role: string) {
      return function (target: any, key: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
          if (role === 'admin') {
            return originalMethod.apply(this, args);
          } else {
            throw new Error('Permission denied');
          }
        };
        return descriptor;
      };
    }
    
    class MyClass {
      @checkPermission('admin')
      adminMethod() {
        return 'Admin access granted';
      }
    }
    
    const instance = new MyClass();
    console.log(instance.adminMethod()); // Throws error if role is not 'admin'
    
  3. 依赖注入:通过类装饰器,可以实现依赖注入的功能。

    function Injectable(target: any) {
      // 这里可以实现依赖注入的逻辑
      console.log(`Injectable class: ${target.name}`);
    }
    
    @Injectable
    class MyService {
      constructor() {}
    }
    
  4. 属性验证:通过属性装饰器,可以在属性赋值时进行验证。

    function validate(target: any, key: string) {
      let value = target[key];
      const getter = () => value;
      const setter = (newValue: string) => {
        if (newValue.length > 10) {
          throw new Error('Value is too long');
        }
        value = newValue;
      };
      Object.defineProperty(target, key, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true,
      });
    }
    
    class MyClass {
      @validate
      myProperty: string;
    }
    
    const instance = new MyClass();
    instance.myProperty = 'Hello'; // Works fine
    instance.myProperty = 'This is too long'; // Throws error
    
  5. 路由绑定:在框架中,装饰器可以用于绑定路由。

    function Route(path: string) {
      return function (target: any, key: string, descriptor: PropertyDescriptor) {
        // 这里可以实现路由绑定的逻辑
        console.log(`Binding route ${path} to method ${key}`);
      };
    }
    
    class MyController {
      @Route('/home')
      home() {
        return 'Home Page';
      }
    }
    

总结

装饰器在 TypeScript 中提供了一种强大的元编程能力,允许开发者在不修改原始代码的情况下,动态地扩展或修改类的行为。它们在框架开发、日志记录、权限控制、依赖注入等场景中非常有用。然而,装饰器目前仍处于实验阶段(Stage 2),因此在生产环境中使用时需要谨慎,并确保 TypeScript 配置中启用了 experimentalDecorators 选项。

标签:TypeScript
上次更新:

相关文章

TypeScript 映射类型常见问题与解决方案 | 提升代码维护性

本文探讨了在使用 TypeScript 时,映射类型的不当使用可能导致的问题,如代码难以维护、类型推断不准确或性能问题,并提供了相应的解决方案和最佳实践。

·编程语言

TypeScript 交叉类型与联合类型:区别与最佳实践

本文详细解释了 TypeScript 中交叉类型(Intersection Types)和联合类型(Union Types)的区别,提供了使用场景、类型守卫、避免过度使用交叉类型的建议,以及如何通过工具辅助解决混淆问题。

·编程语言

TypeScript 类继承中的常见类型问题及解决方案 | TypeScript 开发指南

本文详细探讨了在 TypeScript 中使用类继承时可能遇到的常见类型问题,包括类型兼容性、构造函数、方法重写、访问修饰符、泛型类继承、抽象类以及类型断言等问题,并提供了相应的解决方案和代码示例。

·编程语言

TypeScript 函数重载:常见问题与解决方案

本文探讨了 TypeScript 中函数重载的常见问题,包括签名与实际实现不匹配、重载签名过多、与泛型结合时的类型推断问题等,并提供了相应的解决方案。

·编程语言

TypeScript 类型与泛型约束冲突的解决方法 | 技术指南

本文详细介绍了在 TypeScript 中解决类型与泛型约束冲突的多种方法,包括明确泛型参数的类型约束、使用类型断言、条件类型、默认类型参数等,帮助开发者有效处理类型推断问题。

·编程语言

TypeScript 类型工具函数常见错误及解决方法 | 详细指南

本文详细介绍了在使用 TypeScript 类型工具函数时可能遇到的常见错误,包括类型推断错误、类型工具函数未定义、参数错误、返回类型错误等,并提供了相应的解决方法。

·编程语言

TypeScript 类型与运行时值不匹配的解决策略与最佳实践

本文详细介绍了在 TypeScript 开发中解决类型与运行时值不匹配问题的多种策略和最佳实践,包括类型断言、类型保护、类型推断、运行时类型检查、使用 `unknown` 类型、第三方库、避免 `any` 类型、`as const` 常量断言、`never` 类型处理以及 `readonly` 和 `ReadonlyArray` 的使用。

·编程语言

TypeScript 枚举类型常见问题及解决方案 | 最佳实践指南

本文详细介绍了在使用 TypeScript 枚举类型时可能遇到的常见问题,如枚举值类型不一致、重复、未定义等,并提供了相应的解决方案和最佳实践,帮助开发者编写更健壮和可维护的代码。

·编程语言

TypeScript 类型扩展与合并技巧 - 实用指南

本文详细介绍了在 TypeScript 中处理类型扩展与合并的多种方法,包括使用 `interface`、`type`、`extends`、`Partial`、`Pick`、`Omit`、`Record`、映射类型、条件类型、实用类型和 `namespace`。这些技巧有助于更好地管理和扩展复杂的类型系统。

·编程语言

TypeScript 类型断言的滥用问题及解决策略 | 提高代码类型安全性和可维护性

本文探讨了 TypeScript 中类型断言的滥用问题,并提供了多种解决策略,包括优先使用类型推断、类型保护、类型声明、避免使用 `any` 类型、使用 `unknown` 类型、重构代码、使用工具进行代码审查以及团队培训和代码审查。

·编程语言