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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:17:37