详细讲解 reflect-metadata
的作用、用法,并配合多个实用例子,尤其是它如何结合 TypeScript 装饰器来实现高级功能,比如依赖注入、类型推断、字段注解等。
🧠 一、reflect-metadata
是什么?
reflect-metadata
是一个支持 元编程(Meta Programming) 的库,它允许你给类、属性、方法、函数参数添加“元信息”并在运行时读取这些信息。
大白话:类似于中间件,什么路由中间件啥的,写好后加在那个方法上就能去操作那个方法的或者类啥的能力,比如写个日志打印,添加到的函数都会被打印日志,比如路由中间件本身也是有种元编程能力,加上就能在加上的路由上都执行中间件,或者Refet,可以操作所有对象的属性方法啥的,proxy,被proxy代理的变量都会被监控getset等等,总之就是可以搞其他写好的程序的程序就喊具有元编程能力的程序
🎯 二、应用场景
应用 | 描述 |
---|
🧱 类型系统扩展 | 读取类、方法、参数的类型 |
📦 依赖注入框架 | 自动注入构造函数参数的实例 |
📊 表单/数据校验 | 读取字段类型与校验规则 |
🧩 ORM 映射 | 标注类字段 → 数据库字段 |
🌐 路由控制器 | 使用装饰器定义路由、方法等 |
✅ 三、启用 reflect-metadata 前提
- 安装:
npm install reflect-metadata
- 在入口文件(如
main.ts
)顶部引入:
import 'reflect-metadata'
tsconfig.json
配置:
{"experimentalDecorators": true,"emitDecoratorMetadata": true
}
🚀 四、核心 API 用法示例
✅ 1. 属性元数据(使用装饰器 + metadata)
import 'reflect-metadata'function Label(label: string) {return Reflect.metadata('label', label)
}class User {@Label('用户名')name: string
}
const label = Reflect.getMetadata('label', User.prototype, 'name')
console.log(label)
✅ 2. 读取属性的类型信息(需要 emitDecoratorMetadata)
import 'reflect-metadata'class User {name: stringage: number
}const type1 = Reflect.getMetadata('design:type', User.prototype, 'name')
const type2 = Reflect.getMetadata('design:type', User.prototype, 'age')console.log(type1.name)
console.log(type2.name)
- 这是 TypeScript 编译器自动生成的类型信息。
design:type
是 reflect-metadata 保留的 key。
✅ 3. 读取方法参数类型(用于依赖注入)
import 'reflect-metadata'class LoggerService {}
class UserService {}class App {constructor(public logger: LoggerService, public userService: UserService) {}
}const paramTypes = Reflect.getMetadata('design:paramtypes', App)
console.log(paramTypes.map((t: any) => t.name))
- 在依赖注入框架中,框架就能根据这些类型自动创建和注入实例。
✅ 4. 自定义字段验证装饰器
import 'reflect-metadata'function Required(target: any, key: string) {Reflect.defineMetadata('required', true, target, key)
}function isFieldRequired(target: any, key: string) {return Reflect.getMetadata('required', target, key)
}class Product {@Requiredname: stringprice: number
}console.log(isFieldRequired(Product.prototype, 'name'))
console.log(isFieldRequired(Product.prototype, 'price'))
🌍 五、结合场景示例:构建简易依赖注入容器
import 'reflect-metadata'class Logger {log(msg: string) {console.log('Log:', msg)}
}class Service {constructor(public logger: Logger) {}
}
function create<T>(target: new (...args: any[]) => T): T {const paramTypes = Reflect.getMetadata('design:paramtypes', target) || []const params = paramTypes.map((type: any) => new type())return new target(...params)
}const service = create(Service)
service.logger.log('Hello DI')
🎯 利用 design:paramtypes
读取构造函数参数类型,并自动创建依赖项实例。
⚠️ 六、注意事项
限制 | 描述 |
---|
类型信息只在 TS 编译时生成 | 必须开启 emitDecoratorMetadata |
必须引入 reflect-metadata | 否则 Reflect API 不可用 |
运行时类型是构造函数 | 例如 String 、Number ,不是具体值类型 |
🧾 七、常用内置 Metadata Key
Key | 描述 |
---|
design:type | 属性的类型 |
design:paramtypes | 构造函数或方法的参数类型数组 |
design:returntype | 方法的返回类型 |
✅ 总结
点 | 内容 |
---|
📦 工具 | reflect-metadata 提供在运行时定义/获取元信息的能力 |
🔧 搭配 | 常与装饰器、TypeScript 一起使用 |
💡 应用 | 依赖注入、类型反射、表单校验、ORM 等场景广泛使用 |
📌 核心 API | Reflect.defineMetadata() 、Reflect.getMetadata() 、design:type 等 |
🚀 必备配置 | experimentalDecorators: true + emitDecoratorMetadata: true |