全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-21 8 分钟 ✍️ juanwangdev

TypeScript 装饰器工厂与元数据反射

装饰器工厂与元数据反射是高级装饰器用法,实现依赖注入与 AOP 编程,下面梳理核心用法。

装饰器工厂

装饰器工厂是返回装饰器函数的函数,支持传入配置参数:

TypeScript
function Route(path: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 存储路由信息
    Reflect.defineMetadata("route:path", path, target, propertyKey);
  };
}

class ApiController {
  @Route("/api/users")
  getUsers() {
    return [];
  }
}

多参数工厂

TypeScript
function Validate(required: boolean, type: "string" | "number") {
  return function (target: any, propertyKey: string, parameterIndex: number) {
    const key = `validate:${propertyKey}:${parameterIndex}`;
    Reflect.defineMetadata(key, { required, type }, target);
  };
}

class UserService {
  createUser(@Validate(true, "string") name: string) {}
}

装饰器组合

同一目标可应用多个装饰器,按顺序执行:

TypeScript
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`>> ${key}`);
    const result = original.apply(this, args);
    console.log(`<< ${key}`);
    return result;
  };
}

function Cache(ttl: number) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    const cache = new Map();
    descriptor.value = function (...args: any[]) {
      const cacheKey = JSON.stringify(args);
      if (cache.has(cacheKey)) return cache.get(cacheKey);
      const result = original.apply(this, args);
      cache.set(cacheKey, result);
      setTimeout(() => cache.delete(cacheKey), ttl);
      return result;
    };
  };
}

class DataService {
  @Log
  @Cache(5000)
  fetchData(id: string) {
    return { id, data: "result" };
  }
}
// 执行顺序:Cache 包装 Log 包装 原方法
// 调用:Log >> Cache >> 原方法 >> Cache >> Log <<

注意:装饰器从下到上执行,最靠近方法的先执行。

元数据反射

使用 reflect-metadata 库存储与读取装饰器元数据:

安装

Bash
npm install reflect-metadata

基本用法

TypeScript
import "reflect-metadata";

const ROUTE_KEY = "design:route";

function Get(path: string) {
  return function (target: any, propertyKey: string) {
    Reflect.defineMetadata(ROUTE_KEY, path, target, propertyKey);
  };
}

function Post(path: string) {
  return function (target: any, propertyKey: string) {
    Reflect.defineMetadata(ROUTE_KEY, `POST:${path}`, target, propertyKey);
  };
}

class UserController {
  @Get("/users")
  list() {}

  @Post("/users")
  create() {}
}

// 读取元数据
const path = Reflect.getMetadata(ROUTE_KEY, new UserController(), "list");
console.log(path);  // "/users"

设计时类型元数据

TypeScript
function LogType(target: any, propertyKey: string) {
  const type = Reflect.getMetadata("design:type", target, propertyKey);
  console.log(`${propertyKey} type: ${type.name}`);
}

class User {
  @LogType
  name!: string;  // 输出:name type: String
}
元数据键说明
design:type属性/参数类型
design:paramtypes方法参数类型列表
design:returntype方法返回值类型

依赖注入示例

TypeScript
const INJECT_KEY = "inject";

function Inject(token: any) {
  return function (target: any, propertyKey: string, parameterIndex: number) {
    const params = Reflect.getMetadata(INJECT_KEY, target) || {};
    params[parameterIndex] = token;
    Reflect.defineMetadata(INJECT_KEY, params, target);
  };
}

function createInstance<T>(ctor: new (...args: any[]) => T): T {
  const params = Reflect.getMetadata(INJECT_KEY, ctor) || {};
  const args = Object.keys(params).map(i => new (params[i])());
  return new ctor(...args);
}

class Database {}

class UserRepository {
  constructor(@Inject(Database) private db: Database) {}
}

const repo = createInstance(UserRepository);

要点总结

  • 装饰器工厂返回装饰器函数,支持传入配置参数定制行为
  • 多个装饰器可组合应用,执行顺序从下到上
  • reflect-metadata 提供运行时元数据存储与读取
  • design:type/design:paramtypes/design:returntype 获取设计时类型信息
  • 依赖注入通过装饰器记录参数类型,运行时自动实例化

📝 发现内容有误?点击此处直接编辑

← 上一篇 TypeScript 泛型编程
下一篇 → TypeScript 装饰器详解
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库