当前位置: 首页 > backend >正文

TypeScript 装饰器详解

装饰器(Decorator)是 TypeScript 中的一个重要特性,它提供了一种声明式的方式来修改类、方法、属性或参数的行为。装饰器使用 @expression 语法,其中 expression 是一个函数,它会在运行时被调用,并接收被装饰目标的元数据。

1. 装饰器基础

1.1. 启用装饰器

在 tsconfig.json 中启用实验性装饰器支持:

{"compilerOptions": {"experimentalDecorators": true,"emitDecoratorMetadata": true}
}

2. 装饰器类型

2.1. 类装饰器

类装饰器应用于类构造函数,可以用来观察、修改或替换类定义。

function sealed(constructor: Function) {Object.seal(constructor);Object.seal(constructor.prototype);
}@sealed
class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}greet() {return "Hello, " + this.greeting;}
}

通过定义类装饰器工厂还可以传递参数给装饰器:

function color(value: string) {return function (constructor: Function) {constructor.prototype.color = value;};
}@color("red")
class Car {// ...
}const myCar = new Car();
console.log((myCar as any).color); // 输出: red

2.2. 方法装饰器

方法装饰器应用于方法的属性描述符,可以用来观察、修改或替换方法定义。

function enumerable(value: boolean) {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {descriptor.enumerable = value;};
}class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}@enumerable(false)greet() {return "Hello, " + this.greeting;}
}

2.3. 访问器装饰器

访问器装饰器应用于访问器的属性描述符,可以用来观察、修改或替换访问器的定义。

function configurable(value: boolean) {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {descriptor.configurable = value;};
}class Point {private _x: number;private _y: number;constructor(x: number, y: number) {this._x = x;this._y = y;}@configurable(false)get x() {return this._x;}@configurable(false)get y() {return this._y;}
}

2.4. 属性装饰器

属性装饰器应用于类的属性。

function format(formatString: string) {return function (target: any, propertyKey: string) {let value = target[propertyKey];const getter = function () {return `${formatString} ${value}`;};const setter = function (newVal: string) {value = newVal;};Object.defineProperty(target, propertyKey, {get: getter,set: setter,enumerable: true,configurable: true,});};
}class Greeter {@format("Hello")greeting: string;constructor(message: string) {this.greeting = message;}
}const greeter = new Greeter("World");
console.log(greeter.greeting); // 输出: Hello World

2.5. 参数装饰器

参数装饰器应用于构造函数或方法的一个参数。

function validate(target: any,propertyKey: string,parameterIndex: number
) {const validParams: number[] = Reflect.getOwnMetadata("validParams", target, propertyKey) || [];validParams.push(parameterIndex);Reflect.defineMetadata("validParams", validParams, target, propertyKey);
}class Greeter {greet(@validate name: string) {return "Hello " + name;}
}

3. 装饰器执行顺序

装饰器的应用顺序如下:

1. 参数装饰器,然后依次是方法装饰器、访问器装饰器或属性装饰器应用到每个实例成员;

2. 参数装饰器,然后依次是方法装饰器、访问器装饰器或属性装饰器应用到每个静态成员;

3. 参数装饰器应用到构造函数;

4. 类装饰器应用到类;

4. 实际应用示例

4.1. 日志装饰器

function log(target: any,propertyKey: string,descriptor: PropertyDescriptor
) {const originalMethod = descriptor.value;descriptor.value = function (...args: any[]) {console.log(`Calling ${propertyKey} with args: ${JSON.stringify(args)}`);const result = originalMethod.apply(this, args);console.log(`Called ${propertyKey}, returned: ${JSON.stringify(result)}`);return result;};return descriptor;
}class Calculator {@logadd(a: number, b: number) {return a + b;}
}const calculator = new Calculator();
calculator.add(2, 3);
// 输出:
// Calling add with args: [2,3]
// Called add, returned: 5

4.2. 验证装饰器

function validateEmail(target: any,propertyKey: string,parameterIndex: number
) {const validateParams: number[] = Reflect.getOwnMetadata("validateParams", target, propertyKey) || [];validateParams.push(parameterIndex);Reflect.defineMetadata("validateParams", validateParams, target, propertyKey);
}function validate(target: any,propertyKey: string,descriptor: PropertyDescriptor
) {const originalMethod = descriptor.value;const validateParams: number[] = Reflect.getOwnMetadata("validateParams", target, propertyKey) || [];descriptor.value = function (...args: any[]) {for (const index of validateParams) {if (!/^\S+@\S+\.\S+$/.test(args[index])) {throw new Error(`Invalid email: ${args[index]}`);}}return originalMethod.apply(this, args);};return descriptor;
}class UserService {@validatecreateUser(name: string,@validateEmail email: string,age: number) {return { name, email, age };}
}const userService = new UserService();
userService.createUser("Alice", "alice@example.com", 25); // 正常
userService.createUser("Bob", "invalid-email", 30); // 抛出错误

4.3. 依赖注入装饰器

const serviceRegistry = new Map<string, any>();function Injectable(constructor: Function) {serviceRegistry.set(constructor.name, new constructor());
}function Inject(serviceName: string) {return function (target: any, propertyKey: string) {Object.defineProperty(target, propertyKey, {get: () => serviceRegistry.get(serviceName),enumerable: true,configurable: true,});};
}@Injectable
class LoggerService {log(message: string) {console.log(`[LOG]: ${message}`);}
}class AppComponent {@Inject("LoggerService")logger!: LoggerService;run() {this.logger.log("Application started");}
}const app = new AppComponent();
app.run(); // 输出: [LOG]: Application started

5. 元数据反射 API

TypeScript 与 reflect-metadata 库结合使用可以提供更强大的装饰器功能:

npm install reflect-metadata

然后在入口文件顶部添加:

import "reflect-metadata";

使用示例:

function logType(target: any, key: string) {const type = Reflect.getMetadata("design:type", target, key);console.log(`${key} type: ${type.name}`);
}class Demo {@logTypepublic attr1: string = "hello";@logTypepublic attr2: number = 42;
}// 输出:
// attr1 type: String
// attr2 type: Number

6. 装饰器与 Angular

Angular 框架大量使用装饰器:

import { Component, Input, Output, EventEmitter } from '@angular/core';@Component({selector: 'app-greeter',template: `<h1>Hello {{name}}!</h1><button (click)="onClick()">Click me</button>`
})
export class GreeterComponent {@Input() name: string = 'World';@Output() clicked = new EventEmitter<void>();onClick() {this.clicked.emit();}
}

7. 总结

TypeScript 装饰器提供了强大的元编程能力,可以用于:

1. 添加元数据;

2. 修改或扩展类、方法、属性的行为;

3. 实现 AOP (面向切面编程);

4. 依赖注入;

5. 验证和转换数据;

6. 日志记录和性能监控;

装饰器在 Angular、NestJS 等框架中广泛应用,是 TypeScript 高级开发的重要工具。

http://www.xdnf.cn/news/5412.html

相关文章:

  • 论文阅读与写作:《从探索到突破:解密科研和论文写作的思维密码》
  • 17.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--loki
  • Stream流
  • 一周内学完计算机网络课程之二:计算机网络物理层的理解
  • STM32智能窗帘系统:从零到一的开发实战
  • 如何租用服务器并通过ssh连接远程服务器终端
  • 【计算机网络01】 网络组成与三种交换方式
  • Web 实时通信技术:WebSocket 与 Server-Sent Events (SSE) 深入解析
  • 【RAG】11种Chunking Strategies分块策略介绍和选择
  • VirtualBox中安装并运行ubuntu-24.04.2-desktop虚拟机
  • Linux基础整理
  • Docker Compose 完全指南:从入门到生产实践
  • java的Stream流处理
  • 数据库事务以及JDBC实现事务
  • 模型欠拟合是什么?
  • 基于VSCode + PlatformIO平台的ESP8266的DS1302实时时钟
  • AI日报 · 2025年05月11日|传闻 OpenAI 考虑推出 ChatGPT “永久”订阅模式
  • Linux中常见开发工具简单介绍
  • 05.three官方示例+编辑器+AI快速学习three.js webgl - animation - skinning - ik
  • Three.js + React 实战系列 - 职业经历区实现解析 Experience 组件✨(互动动作 + 3D 角色 + 点击切换动画)
  • MySql(进阶)
  • 【大模型】AI智能体Coze 知识库从使用到实战详解
  • 基于Dify实现对Excel的数据分析
  • 嵌入式硬件篇---陀螺仪|PID
  • Linux之进程概念
  • 电源架构与太阳能充电器电路设计分析
  • OpenWrt开发第8篇:树莓派开发板做无线接入点
  • langchain 接入国内搜索api——百度AI搜索
  • Qt 样式表:全面解析与应用指南
  • LeetCode 高频题实战:如何优雅地序列化和反序列化字符串数组?