将元数据添加到 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
继承自 game
而 act
是一个用 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 提供的一个非常有用的构造,虽然是实验性的。
我在 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
继承自 game
而 act
是一个用 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 提供的一个非常有用的构造,虽然是实验性的。