我正在开发一个图书馆,该图书馆将实现一些自定义的Web请求路由设置。而且我希望能够使用像这样的typecirpt装饰器来实现此功能。
@Controller({ path: '/api' })
class TestController {
@Route('get', '/')
get() {
return 'banana';
}
}
我遇到的问题是,我似乎无法将'child'方法修饰器链接到'parent'类修饰器。
我有一些非常简单的装饰器工厂,您可以在这里看到:
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;
}
现在的问题是,每个装饰器实例返回的目标类型都有些许不同,这意味着我无法对其进行比较。 类方法为我的类返回一个Function签名,而该方法返回一个命名对象签名。
有什么我想念的东西吗?我看过其他图书馆也可以进行这种链接,所以我知道应该可以!
答案 0 :(得分:1)
当然,发布后不久,我会处理您的问题。我只需要将“父”类目标的原型与“子”方法目标进行比较,它们就会匹配。
答案 1 :(得分:1)
我以前实际上遇到过这个确切的问题,并且有很多复杂的问题。
首先,可以,您很容易失去对“ this”值的访问权限,因此必须小心。另一种方法是将每个函数视为恰好在对象中定义的静态纯方法。第二个是装饰器的评估顺序,就像您可能已经知道的那样,它由内而外。
记住这两个,这就是我所做的。我在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;
}
}
您正在以一种可以阅读和理解的格式定义有关该类的其他信息。您在类内部使用的每种方法/属性都需要存储有关其自身的信息,并且 NOT 执行 ANY 操作。装饰器决不能引起任何外部副作用 ever 。我之所以仅将此作为重点,是因为您不想失去对代码库中事情发生情况的了解。
现在我们要看一些代码,我们必须避开讨厌的注册,并且不要失去对某些潜在绑定代码的访问权限。我们可以通过在类上新创建的__module
属性来获得所需的一切,但是尚不能通过typescript看到。
此处有两个选项:
let myInstance: IServerModule & MyClass = new MyClass();
// or
let myInstance: any = new MyClass();
但是,当您访问方法注册(express.get等)时,您想要一个引用该类的东西,将其存储在注册表中(实际上只是一些启动文件中的一个数组,类似于Angular的模块) ),然后将所有内容注册到该引导文件/模块文件中。
访问__module
属性,读取存储的信息,然后根据需要进行注册。这样,您就可以将关注点分离,对应用程序中正在创建的内容有了清晰的了解,并可以完全根据自己的需要使用装饰器。