将元数据添加到 Typescript 中的方法

Add metadata to method in Typescript

我在 Typescript 中有一个 class,我需要在其中标记一些方法,以确定它们是否可以在服务器、客户端或两者上 运行。我打算使用装饰器,所以我可以用以下标记相关方法:

class Game {
  @serverAction
  getRandomNumber(): number { return 4; }

  @clientAction
  pass() { }
}

等将值包装在装饰器中让我有些方法,但我仍然需要能够从方法外部(并且可能在 class 之外)识别哪些装饰器被赋予了方法。

如何使这些装饰器可访问?

我的第一次尝试是在装饰器添加的 class 中包含一个静态字典,但这会导致问题 - 因为我预计 class 会被多次继承。我的第二次尝试是将它添加到装饰器方法中的 PropertyDescriptor 中:

export function clientAction(target: Game, propertyKey: string, descriptor: any) {
    descriptor.custom = "Test"
}

但是当我尝试使用 Object.getOwnPropertyDescriptor(testGame.act, 'custom') 访问它时(其中 testGame 继承自 gameact 是一个用 clientAction 修饰的方法),它返回未定义。

问题是 属性 描述符不能用于保存它提供的数据以外的额外数据,使其不可变。我建议使用 typescripts' reflect-meta which is for this specific case, adding meta data to decorated properties. You can read more on it here and its npm link.

这是一个使用案例:

说你想按照你说的那样标记你的装饰器,这样你就可以引用数据 例如,在稍后阶段的另一个地方,首先您需要编写将添加数据的函数。

// you add the args that will be needed in the function
export function name(data: any) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    Reflect.defineMetadata(
      // this here is to reference the data later when we retrieve it.
      propertyKey,
      {
        // we put this spread operator in case you have decorated already, so
        // we dont want to lose the old data
        ...Reflect.getMetadata(propertyKey, target),
        // then we append whatever else we need
        name: data,
      },
      target,
    );
    return descriptor;
  };
}

也就是说我们可以用一些方法定义一个 class 并装饰它

class Name {
  start = 'Hello';

  @name('John Doe')
  display() {
    console.log(this.start);
  }
}

所以我们已经完成了上面的操作,现在我们想附加装饰字符串 到 start 属性 以便在打印时添加 string 在装饰器中也是如此。

// so first we get an instance of the class
const a = new Name();

// then we have the get the data

// this will get all the keys that have meta data, which will be 'display'
const keys = Reflect.getMetadataKeys(a);
// then we loop through to get that keys data,
keys.forEach((k) => {
  const data = Reflect.getMetadata(k, a);
  // now we have the meta data so we can append it.

  a.start = `${a.start} ${data.name}`;
});

// and finally, we can call display
a.display(); // 'Hello John Doe'

这是 typescript 提供的一个非常有用的构造,虽然是实验性的。