全部学科
Python全栈
python
NodeJS全栈
nodejs
📅 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 常用内置工具类型
扫码体验小程序
加载中
想在手机上刷题学习?
使用微信卷王开发者小程序,打开首页顶部扫码功能识别二维码