You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript自定义类装饰器工厂:添加静态元数据并运行时读取

嘿,我完全懂你要的是什么——就是像Angular的__annotations__那样,给类类型本身附加静态元数据,不用实例化就能在运行时读取对吧?刚好我之前做过类似的实现,给你一步步拆解具体怎么做:

1. 先明确元数据结构

首先我们可以模仿Angular的风格,定义一个元数据接口,比如组件元数据:

// 定义组件元数据的结构,你可以根据需求扩展
interface ComponentMetadata {
  selector?: string;
  template?: string;
  styleUrls?: string[];
  providers?: any[];
}

2. 实现自定义类装饰器工厂

装饰器工厂的核心是接收元数据参数,然后返回一个类装饰器——这个类装饰器的参数就是类本身(构造函数),我们直接给它添加静态属性来存储元数据:

// 装饰器工厂:接收元数据,返回类装饰器
function Component(metadata: ComponentMetadata): ClassDecorator {
  return function (target: Function) {
    // 模仿Angular的__annotations__,先检查类是否已有该属性
    if (!target.hasOwnProperty('__annotations__')) {
      // 用Object.defineProperty确保属性行为和Angular一致(不可枚举、可写)
      Object.defineProperty(target, '__annotations__', {
        value: [],
        writable: true,
        enumerable: false, // 避免被for-in等遍历到
        configurable: true
      });
    }
    // 将元数据推入数组(支持多个装饰器叠加)
    (target as any).__annotations__.push(metadata);
  };
}

3. 使用装饰器给类添加元数据

现在就可以像Angular那样给类加装饰器了,元数据会直接附着在类本身:

// 给AppComponent添加组件元数据
@Component({
  selector: 'app-root',
  template: '<h1>Hello Custom Decorators!</h1>',
  styleUrls: ['./app.component.css'],
  providers: []
})
class AppComponent {
  // 类的实例属性和方法,不影响元数据
  appTitle = 'My TypeScript App';
}

4. 无需实例化读取元数据

重点来了!我们不需要创建类的实例,直接从类的静态属性__annotations__里读取元数据:

// 直接访问类的静态元数据,完全不需要new AppComponent()
const componentMetadata = (AppComponent as any).__annotations__[0];

console.log(componentMetadata.selector); // 输出: app-root
console.log(componentMetadata.template); // 输出: <h1>Hello Custom Decorators!</h1>

5. 扩展:通用元数据装饰器

如果需要给不同类型的类(比如服务、指令)加元数据,可以写一个通用的装饰器工厂:

// 通用类元数据装饰器工厂,支持自定义元数据键
function ClassMetadata<T>(metadataKey: string, metadata: T): ClassDecorator {
  return function (target: Function) {
    if (!target.hasOwnProperty(metadataKey)) {
      Object.defineProperty(target, metadataKey, {
        value: [],
        writable: true,
        enumerable: false,
        configurable: true
      });
    }
    (target as any)[metadataKey].push(metadata);
  };
}

// 给服务添加元数据
ClassMetadata('__serviceMetadata__', { providedIn: 'root' })
class DataService {
  fetchData() {
    return Promise.resolve('Sample data');
  }
}

// 读取服务元数据
const serviceMeta = (DataService as any).__serviceMetadata__[0];
console.log(serviceMeta.providedIn); // 输出: root

6. TypeScript配置注意事项

要让装饰器正常工作,需要在tsconfig.json里启用实验性装饰器:

{
  "compilerOptions": {
    "target": "ES5" /* 或更高版本 */,
    "experimentalDecorators": true, // 必须开启
    "emitDecoratorMetadata": false, // 我们手动管理元数据,这个可以关闭
    "strict": true
  }
}

这样实现的装饰器工厂就完全符合你的需求:元数据直接挂载在类本身的静态属性上,不需要实例化就能读取,和Angular的__annotations__行为一致。

内容的提问来源于stack exchange,提问作者Lenny D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 18:47:45