我正在开发一个库,它将实现一些定制的web请求路由设置。我希望能够使用这样的类型圈装饰器来实现这个功能。
@Controller({ path: '/api' })
class TestController {
@Route('get', '/')
get() {
return 'banana';
}
}我遇到的问题是,我似乎无法将“子”方法装饰器与“父”类装饰器连接起来。
我有一些很简单的装饰工厂,你可以在这里看到:
export function Controller(params?: IControllerParams) {
const func: ClassDecorator = (target) => {
registerController(target, params || {});
logger.info(`Registered controller: ${target.name}`);
console.dir(target); // [Function: TestController]
};
return func;
}
export function Route(verb: Verb, path: string) {
const func: MethodDecorator = (target, key) => {
registerRoute(verb, path, key, target);
logger.info(`Registered route: ${path} for verb: ${verb}`);
console.dir(target); // TestController {}
};
return func;
}现在的问题是,每个装饰器实例化返回的目标类型都非常不同,这意味着我无法对它们进行比较。类方法返回我的类的函数签名,方法返回命名的对象签名。
我遗漏了什么吗?我见过其他库做这种链接,所以我知道这应该是可能的!
发布于 2019-03-31 18:13:40
我以前确实遇到过这个问题,而且有很多复杂的问题。
首先,您可能很容易失去“此”值的访问权限,因此您必须小心。另一种方法是将每个函数视为一个静态的、纯的方法,该方法恰好是在一个对象中定义的。第二个问题是对装饰者进行评估的顺序,正如您可能已经知道的那样,这个顺序是从内到外的。
考虑到这两点,我就是这么做的。我在Meteor中使用了这个代码,它做了一些与您所做的非常相似的事情。
前言
ServerModule只是一个具有一系列方法处理程序的类。也就是控制器,这个代码是为Meteor构建的。
“守则”
/**
* This is horribly ugly code that I hate reading myself,
* but it is very straightforward. It defines a getter
* property called __modulle, and returns the data that
* we care about in a format that is readable for a future
* registry/bootstrapping system
*/
function boltModuleProperty(proto: any) {
Object.defineProperty(proto, '__module', {
get: function () {
let obj: IModuleDetails = {};
for (let key in this.__moduleFunctions)
obj[`${this.__moduleName}.${key}`] = this.__moduleFunctions[key];
return obj;
}
})
}
/**
* This is evaluated at the very end.
*
* Collect all the methods and publications, registering
* them with Meteor so they become available via the
* default Meteor Methods and Subscriptions.
*/
export function ServerModule (moduleName?: string) {
return function (target: any) {
boltModuleProperty(target.prototype);
// Use either a passed-in name, or the class' name
target.prototype.__moduleName = moduleName || target.name;
}
}
/**
* Take the name of the method to be exposed for Meteor,
* and save it to the object's prototype for later. We
* we do this so we can access each method for future
* registration with Meteor's 'method' function
*/
export function ServerMethod (name: string = null) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
let fnName = name || descriptor.value.name;
// ensure we actually get the real prototype
let proto = target.prototype ? target.prototype : target.constructor.prototype
if (!proto.__moduleFunctions) proto.__moduleFunctions = {};
proto.__moduleFunctions[fnName] = descriptor.value;
}
}解释
您正在以您能够阅读和理解的格式定义有关该类的其他信息。类中使用的每个方法/属性都需要存储有关自身的信息,而不是执行任何操作。装潢师永远不应造成任何外部的副作用。我只想说明这一点,因为您不想忘记代码库中的事情是如何发生的。
现在我们有了一些代码要看,我们必须绕开那些烦人的注册,不要失去对一些潜在绑定代码的访问。我们已经通过类上新创建的__module属性获得了所需的一切,但是它还不能通过类型记录显示。
这里有两种选择:
let myInstance: IServerModule & MyClass = new MyClass();
// or
let myInstance: any = new MyClass();设置它
不管您访问的是方法注册(express.get,等等),您都希望有一些内容能够引用类,将其存储在注册表中(实际上只是某个引导文件中的数组,类似于角的模块),并注册该引导/模块文件中的所有内容。
访问__module属性,读取所存储的信息,并根据需要注册它。通过这种方式,您可以完成关注点的分离,您可以清楚地了解在您的应用程序中创建了什么,并且您可以在您认为必要的时候使用您的装饰器。
发布于 2019-03-31 11:42:22
当然,我在发帖后不久就开始处理这个问题了。我只需将“父”类目标的原型与“子”方法目标进行比较,它们就匹配了。
https://stackoverflow.com/questions/55436357
复制相似问题