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
相关产品推荐
相关产品推荐

