装饰器元编程:从入门到实战,完整掌握元数据反射与运行时类型操作
核心结论:装饰器是实现元编程最强大的特性,配合-库,可以在运行时读取、修改、添加类的元数据,实现依赖注入、验证、序列化、路由绑定等高级功能。本文基于 5.0+官方规范,提供可直接运行的完整示例和最佳实践。
1. 什么是装饰器元编程?
元编程是指编写能够操作、生成或转换其他代码的代码。装饰器本质上是一个函数,它接收目标类、方法、属性或参数的信息,并可以在编译时(实际是运行时)对这些成员进行修改、增强或添加元数据。
核心能力:
在类定义时拦截并修改类的行为
给类、方法、属性附加自定义元数据(通过元数据反射API)
根据元数据在运行时执行逻辑(如自动验证、依赖注入)
启用装饰器(.json):
{
"": {
"rs": true, // 启用装饰器实验特性
"a": true, // 生成元数据(需要-)
"": "ES5" // 或更高,推荐
}
}
> 来源:官方文档 –
2. 五种装饰器类型完整解析
以下按执行顺序和参数结构说明,所有示例均可直接复制运行。
2.1 类装饰器
参数::
执行时机:类声明时,在类定义之后立即执行
返回值:如果返回一个新的构造函数,将替换原类的构造函数
// 基础示例:给类添加静态属性
(: ) {
.log(类 ${.name} 被创建);
.. = new Date();
}
// 替换构造函数的示例
<T { new(...args: any[]): {} }>(: T) {
class {
= new Date();
};
}
@
@
class User {
name: ;
(name: ) { this.name = name; }
}
2.2 方法装饰器
参数:
: (对于静态方法,是类的构造函数;对于实例方法,是类的原型对象)
: |
:
典型用途:日志、性能监控、权限校验、缓存
(: any, : , : ) {
const = .value;
.value = (...args: any[]) {
.log(调用 ${},参数:, args);
const = .apply(this, args);
.log(返回值:, );
;
};
;
}
class {
@
add(a: , b: ) { a + b; }
}
2.3 属性装饰器
参数:
: (同上)
: |
无法直接修改属性值,通常用于配合元数据记录字段类型或验证规则。
(: any, : ) {
// 记录哪些属性是必需的
const = . || [];
.push();
. = ;
}
class User {
@
name: ;
age?: ;
}
2.4 参数装饰器
参数:
:
: | (方法名)
: (参数在参数列表中的索引)
用途:配合方法装饰器,注入特定参数或标记参数用途。
(: any, : , : ) {
// 记录需要注入的参数位置
const = . || {};
[] = true;
. = ;
}
class {
(@ id: ) { / ... / }
}
2.5 装饰器工厂(带参数的装饰器)
通过外层函数接收自定义参数,返回真正的装饰器函数。
Log(level: 'info' | 'error') {
(: any, : , : ) {
const = .value;
.value = (...args: any[]) {
level;
.apply(this, args);
};
};
}
class {
@Log('info')
info() { / ... / }
}
3. 元数据反射:- 完整指南
装饰器本身只能操作代码结构,要真正实现元编程,必须配合元数据反射API。- 是TC39提案的,官方推荐使用。
3.1 安装与导入
npm -
'-'; // 必须在入口文件最顶部导入一次
3.2 核心API
| API | 说明 |
|---|---|
.(, value, , ?) |
定义元数据 |
.(, , ?) |
读取元数据 |
.(, , ?) |
检查是否存在元数据 |
.(, ?) |
获取所有元数据键 |
.(, , ?) |
删除元数据 |
3.3 自动类型元数据(a)
当启用a时,会自动生成以下元数据:
:type:属性或参数的类型
::方法参数的类型数组
::方法的返回类型
'-';
class {
@.(':type', )
name: ;
}
// 自动捕获类型
class Demo {
(param: ): { 1; }
}
const = .(':', Demo., '');
.log(); // []
const = .(':', Demo., '');
.log(); //
4. 实战应用:三个完整的生产级示例
4.1 依赖注入容器(基于装饰器元编程)
'-';
// 定义注入令牌
const = ':token';
// 参数装饰器:标记需要注入的服务
(token: ) {
(: any, : , : ) => {
const = .(, , ) || [];
[] = token;
.(, , , );
};
}
// 类装饰器:自动解析依赖
(token: ) {
(: any) => {
.(':token', token, );
// 保存构造函数供容器使用
;
};
}
// 容器实现
class {
= new Map<, any>();
(token: , : any) {
this..set(token, );
}
<T>(: { new(...args: any[]): T }): T {
const = .(':', ) || [];
const = .(, ., '') || [];
const args = .map((type: any, idx: ) => {
const token = [idx];
if (token) {
const = this..get(token);
this.();
}
new type();
});
new (...args);
}
}
// 使用示例
@('')
class {
() { ['Alice', 'Bob']; }
}
class {
(@('') : ) {}
list() { this..(); }
}
const = new ();
.('', );
const ctrl = .();
.log(ctrl.list()); // ['Alice', 'Bob']
4.2 运行时验证框架
'-';
const = ':rules';
// 验证规则装饰器
(: ) {
(: any, : ) => {
const rules = .(, , ) || [];
rules.push({ type: '', value: });
.(, rules, , );
};
}
(: any, : ) {
const rules = .(, , ) || [];
rules.push({ type: 'email' });
.(, rules, , );
}
// 验证执行器
(: any): { valid: ; : [] } {
const : [] = [];
const = .();
const = .keys();
for (const prop of ) {
const rules = .(, , prop) || [];
const value = [prop];
for (const rule of rules) {
if (rule.type === '' && value === '' && value. < rule.value) {
.push(${prop} 长度不能小于 ${rule.value});
}
if (rule.type === 'email') {
const = /^[^s@]+@([^s@.,]+.)+[^s@.,]{2,}$/;
if (!.test(value)) .push(${prop} 不是有效的邮箱);
}
}
}
{ valid: . === 0, };
}
class {
@(3)
: ;
@
email: ;
(: , email: ) {
this. = ;
this.email = email;
}
}
const form = new ('a', '');
.log((form));
// { valid: false, : [' 长度不能小于 3', 'email 不是有效的邮箱'] }
4.3 自动路由绑定(类似风格)
'-';
const = 'route:';
const = 'route:';
(: ) {
(: any) => {
.(, , );
};
}
Get(path: ) {
(: any, : , : ) => {
.(, { : 'GET', path }, , );
};
}
Post(path: ) {
(: any, : , : ) => {
.(, { : 'POST', path }, , );
};
}
// 路由注册器
(: any, app: any) {
const = .(, ) || '';
const = new ();
const = .;
const = .().(p => p !== '');
for (const of ) {
const = .(, , );
if () {
const = + .path;
app..());
.log(注册路由 ${.} ${} -> ${.name}.${});
}
}
}
// 模拟
const = {
get: (path: , : ) => .log(GET ${path}),
post: (path: , : ) => .log(POST ${path})
};
@('/users')
class {
@Get('/')
list() { ['user1']; }
@Post('/')
(body: any) { { id: 1 }; }
}
(, );
// 输出:
// GET /users/ -> .list
// POST /users/ -> .
5. 常见问题与最佳实践
5.1 装饰器执行顺序
多个装饰器按以下顺序执行:
1. 参数装饰器(从后往前)
2. 方法装饰器
3. 属性装饰器
4. 类装饰器
同一目标上的多个装饰器:先执行靠近目标的装饰器工厂(从内到外),然后执行装饰器函数(从下到上)。
@
@
class Test {
@
@
() {}
}
// 执行顺序:工厂 -> 工厂 -> 函数 -> 函数 -> ->
5.2 性能注意事项
装饰器在类定义时执行一次,不会影响实例化性能
元数据会存储在内存中,大量使用需注意内存占用
避免在装饰器内部执行重操作(如文件I/O、复杂计算)
5.3 与互操作
装饰器是实验特性,未来标准可能与当前实现不同。为确保兼容:
锁定版本(推荐5.x)
避免依赖未文档化的内部行为
对于生产项目,使用官方支持的-并关注提案进展
5.4 调试技巧
在.json中启用"": true,然后在浏览器或Node中设置断点,可以进入装饰器函数内部调试。
# Node调试
node ---brk -r ts-node/ -r -/ your-file.ts
6. 总结与延伸学习
核心要点回顾:
1. 装饰器是函数,分为五类:类、方法、属性、参数、工厂
2. - 是实现高级元编程的基石,提供运行时元数据读写
3. 启用a可自动捕获类型信息
4. 典型应用:依赖注入、验证、路由、序列化、日志、权限
下一步学习资源:
开源框架源码:、、
立即动手:复制本文中的任一示例到你的项目中,修改装饰器逻辑,观察行为变化。只有亲手编写才能完全掌握装饰器元编程的精髓。

