装饰器用法

74 阅读3分钟

** reflect-metadata 的作用、用法,并配合多个实用例子**,尤其是它如何结合 TypeScript 装饰器来实现高级功能,比如依赖注入、类型推断、字段注解等。


🧠 一、reflect-metadata 是什么?

reflect-metadata 是一个支持 元编程(Meta Programming) 的库,它允许你给类、属性、方法、函数参数添加“元信息”并在运行时读取这些信息。 大白话:类似于中间件,什么路由中间件啥的,写好后加在那个方法上就能去操作那个方法的或者类啥的能力,比如写个日志打印,添加到的函数都会被打印日志,比如路由中间件本身也是有种元编程能力,加上就能在加上的路由上都执行中间件,或者Refet,可以操作所有对象的属性方法啥的,proxy,被proxy代理的变量都会被监控getset等等,总之就是可以搞其他写好的程序的程序就喊具有元编程能力的程序

🎯 二、应用场景

应用描述
🧱 类型系统扩展读取类、方法、参数的类型
📦 依赖注入框架自动注入构造函数参数的实例
📊 表单/数据校验读取字段类型与校验规则
🧩 ORM 映射标注类字段 → 数据库字段
🌐 路由控制器使用装饰器定义路由、方法等

✅ 三、启用 reflect-metadata 前提

  1. 安装:
npm install reflect-metadata
  1. 在入口文件(如 main.ts)顶部引入:
import 'reflect-metadata'
  1. 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
}

// 获取 name 字段的自定义元信息
const label = Reflect.getMetadata('label', User.prototype, 'name')
console.log(label) // 输出:用户名

✅ 2. 读取属性的类型信息(需要 emitDecoratorMetadata)

import 'reflect-metadata'

class User {
  name: string
  age: number
}

const type1 = Reflect.getMetadata('design:type', User.prototype, 'name')
const type2 = Reflect.getMetadata('design:type', User.prototype, 'age')

console.log(type1.name) // String
console.log(type2.name) // Number
  • 这是 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)) // ['LoggerService', 'UserService']
  • 在依赖注入框架中,框架就能根据这些类型自动创建和注入实例。

✅ 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 {
  @Required
  name: string

  price: number
}

console.log(isFieldRequired(Product.prototype, 'name')) // true
console.log(isFieldRequired(Product.prototype, 'price')) // undefined

🌍 五、结合场景示例:构建简易依赖注入容器

import 'reflect-metadata'

class Logger {
  log(msg: string) {
    console.log('Log:', msg)
  }
}

class Service {
  constructor(public logger: Logger) {}
}

// 简易 DI 容器
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') // 输出:Log: Hello DI

🎯 利用 design:paramtypes 读取构造函数参数类型,并自动创建依赖项实例。


⚠️ 六、注意事项

限制描述
类型信息只在 TS 编译时生成必须开启 emitDecoratorMetadata
必须引入 reflect-metadata否则 Reflect API 不可用
运行时类型是构造函数例如 StringNumber,不是具体值类型

🧾 七、常用内置 Metadata Key

Key描述
design:type属性的类型
design:paramtypes构造函数或方法的参数类型数组
design:returntype方法的返回类型

✅ 总结

内容
📦 工具reflect-metadata 提供在运行时定义/获取元信息的能力
🔧 搭配常与装饰器、TypeScript 一起使用
💡 应用依赖注入、类型反射、表单校验、ORM 等场景广泛使用
📌 核心 APIReflect.defineMetadata()Reflect.getMetadata()design:type
🚀 必备配置experimentalDecorators: true + emitDecoratorMetadata: true