Cha*_*nan 3 decorator typescript
我正在用 Typescript 创建一个 WebSocket 服务器,其中不同的应用程序组件应该能够注册自己的请求处理程序。有一个WebsocketHandler提供这种行为的单身人士。
没有装饰器,一个类可以像这样注册它的请求处理程序:
class ListOfStuff {
private list = [];
constructor() {
//Register listLengthRequest as a request handler
WebsocketHandler.getInstance().registerRequestHandler('getListLength', () => this.listLengthRequest());
}
private listLengthRequest() : WebSocketResponse {
return new WebSocketResponse(this.list.length);
}
}
class WebsocketHandler {
private constructor() {}
private static instance = new WebsocketHandler();
static getInstance() {
return this.instance;
}
registerRequestHandler(requestName: string, handler: () => WebSocketResponse) {
//Store this handler in a map for when a request is received later
}
}
class WebSocketResponse {
constructor(content: any) {}
}
Run Code Online (Sandbox Code Playgroud)
哪个工作正常。但是,我试图用方法装饰器替换构造函数中的注册调用。理想情况下,ListOfStuff 将如下所示:
class ListOfStuff {
private list = [];
@websocketRequest("getListLength")
private listLengthRequest() : WebSocketResponse {
return new WebSocketResponse(this.list.length);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,在为 创build了一个装饰器工厂之后@websocketRequest,我无法弄清楚如何listLengthRequest()在正确的上下文中执行。我试过这个工厂功能:
function websocketRequest(requestName: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
WebsocketHandler.getInstance().registerRequestHandler(requestName, descriptor.value);
}
}
Run Code Online (Sandbox Code Playgroud)
这使得this等于函数被保存在(内部WebsocketHandler)的地图。
然后我尝试target使用这个工厂函数作为处理程序的上下文传入:
function websocketRequest(requestName: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
WebsocketHandler.getInstance().registerRequestHandler(requestName, () => descriptor.value.call(target));
}
}
Run Code Online (Sandbox Code Playgroud)
但后来我意识到这target只是指代的原型,ListOfStuff而不是实际的类实例。所以仍然没有帮助。
有什么方法可以在装饰器工厂中ListOfStuff获取(以便我可以访问this.list)的实例?我应该以其他方式构造我的装饰器,以便它与类的实例而不是它的原型相关联吗?这是一个演示该问题的 Repl https://repl.it/@Chap/WonderfulLuckyDatalogs
这是我第一次搞乱装饰器,而且我对 Typescript 也很陌生,所以任何指导都将不胜感激。谢谢!
小智 22
以下代码片段显示了我们如何从方法的装饰器中访问目标类实例:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalValue = descriptor.value;
descriptor.value = function(...args: any[]) {
// "this" here will refer to the class instance
console.log(this.constructor.name);
return originalValue.apply(this, args);
}
};
Run Code Online (Sandbox Code Playgroud)
用法:
class Foo {
@log
bar() {
// do something
}
}
Run Code Online (Sandbox Code Playgroud)
无法访问 Typescript 方法装饰器中的实例。但是可以使用装饰器更改原型和构造函数。所以可能的解决方案是使用两个装饰器:第一个“标记”方法,第二个改变构造函数添加注册逻辑。
下面我将尝试说明这个想法
const SubMethods = Symbol('SubMethods'); // just to be sure there won't be collisions
function WebsocketRequest(requestName: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[SubMethods] = target[SubMethods] || new Map();
// Here we just add some information that class decorator will use
target[SubMethods].set(propertyKey, requestName);
};
}
function WebSocketListener<T extends { new(...args: any[]): {} }>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
const subMethods = Base.prototype[SubMethods];
if (subMethods) {
subMethods.forEach((requestName: string, method: string) => {
WebsocketHandler.getInstance()
.registerRequestHandler(
requestName,
() => (this as any)[method]()
);
});
}
}
};
}
Run Code Online (Sandbox Code Playgroud)
用法:
@WebsocketListener
class ListOfStuff {
private list = [];
@WebsocketRequest("getListLength")
private listLengthRequest() : WebSocketResponse {
return new WebSocketResponse(this.list.length);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1476 次 |
| 最近记录: |