TypeScript如何通过类装饰器获取带指定装饰器的属性并修改类
当然可行!这在TypeScript(或者原生JavaScript)里完全能实现,核心是利用装饰器的执行顺序和元数据(或自定义标记)来追踪被标记的属性。下面我给你拆解实现思路和具体代码示例:
实现思路与代码示例
1. 先实现属性装饰器PropertyThing
这个装饰器的作用是给被标记的属性“打标签”,把它们的名字收集起来,方便后续类装饰器读取。我们可以用TypeScript的reflect-metadata库来存储元数据(这是规范方式);如果是原生JS,也可以直接给类原型加自定义属性来存储。
TypeScript版本依赖
首先需要安装元数据库:
npm install reflect-metadata
完整代码示例(TypeScript)
import 'reflect-metadata'; // 属性装饰器:标记需要追踪的属性 function PropertyThing(target: any, propertyKey: string) { // 读取已有的标记属性列表,没有则初始化空数组 const existingMarkedProps = Reflect.getMetadata('tracked-properties', target) || []; // 将当前属性名加入列表,更新元数据 Reflect.defineMetadata('tracked-properties', [...existingMarkedProps, propertyKey], target); } // 类装饰器:收集标记属性并扩展类 function CheckForPropertyThings<T extends { new (...args: any[]): {} }>(target: T) { // 从类的原型上读取所有被标记的属性(实例属性存在原型上) const trackedProperties = Reflect.getMetadata('tracked-properties', target.prototype) || []; // 返回一个继承原类的新类,添加额外属性/方法 return class extends target { // 静态属性:存储所有被标记的属性名 static trackedPropertyList = trackedProperties; // 实例方法:返回标记属性列表 getTrackedProperties() { return trackedProperties; } // 实例属性:类装饰器添加的额外属性 customExtraProperty = '这是类装饰器注入的额外属性'; }; } // 使用示例 @CheckForPropertyThings export class FooBar { @PropertyThing hello: string; @PropertyThing world: number; // 未被标记的普通属性 normalProp: boolean; } // 测试验证 const fooBarInstance = new FooBar(); console.log(FooBar.trackedPropertyList); // 输出: ["hello", "world"] console.log(fooBarInstance.getTrackedProperties()); // 输出: ["hello", "world"] console.log(fooBarInstance.customExtraProperty); // 输出: "这是类装饰器注入的额外属性"
原生JavaScript版本(无需TS)
如果不用TypeScript,也可以用自定义属性来存储标记的属性,实现逻辑类似:
// 属性装饰器 function PropertyThing(target, propertyKey) { // 给原型添加自定义属性存储标记列表 if (!target._trackedProperties) { target._trackedProperties = []; } target._trackedProperties.push(propertyKey); } // 类装饰器 function CheckForPropertyThings(target) { const trackedProperties = target.prototype._trackedProperties || []; // 添加静态属性 target.trackedPropertyList = trackedProperties; // 给原型添加实例属性和方法 target.prototype.customExtraProperty = 'JS版本的额外属性'; target.prototype.getTrackedProperties = function() { return trackedProperties; }; return target; } // 使用示例 @CheckForPropertyThings class FooBar { constructor() { this.hello = ''; this.world = 0; this.normalProp = false; } } // 手动应用属性装饰器(原生JS中类字段装饰器需要手动执行) PropertyThing(FooBar.prototype, 'hello'); PropertyThing(FooBar.prototype, 'world'); // 测试 const instance = new FooBar(); console.log(FooBar.trackedPropertyList); // ["hello", "world"] console.log(instance.customExtraProperty); // "JS版本的额外属性"
关键注意点
- 装饰器执行顺序:属性装饰器会先于类装饰器执行,所以当类装饰器运行时,所有被
PropertyThing标记的属性已经被收集完毕,这是整个方案能生效的核心。 - 属性存储位置:实例属性的装饰器作用在类的原型上,静态属性的装饰器则直接作用在类本身,如果需要处理静态属性,要调整元数据的读取位置(从
target而不是target.prototype读取)。 - 类扩展方式:类装饰器可以选择直接修改原类的原型/静态属性,也可以返回一个新的子类来扩展,后者更符合开闭原则,也更灵活。
内容的提问来源于stack exchange,提问作者kbdsharp
相关产品推荐
相关产品推荐

