Я действительно сталкивался с этой проблемой прежде, и есть значительные осложнения.
Во-первых, может очень легко потерять доступ к значению "this", поэтому вы должны быть осторожны. Альтернатива состоит в том, чтобы рассматривать каждую функцию как статический, чистый метод, который просто определен в объекте. Второй - это порядок оценки декораторов, который, как вы, вероятно, уже знаете, вывернут наизнанку.
Имея в виду оба из них, это то, что я сделал. Я использовал этот код с Meteor, который сделал нечто очень похожее на то, что вы делаете.
Преамбула
ServerModule был просто классом с серией обработчиков методов. AKA контроллер, и этот код был построен для 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 действия. Декоратор никогда не должен вызывать какого-либо внешнего побочного эффекта когда-либо . Я только делаю это важным моментом, потому что вы не хотите забывать о том, как все происходит в вашей кодовой базе.
Теперь, когда у нас есть некоторый код для просмотра, мы должны обойти эту надоедливую регистрацию и не потерять доступ к некоторому потенциально связанному коду. У нас есть все, что нам нужно, через только что созданное свойство __module
класса, но оно пока не видно в машинописи.
Два варианта здесь:
let myInstance: IServerModule & MyClass = new MyClass();
// or
let myInstance: any = new MyClass();
Настройка
Как бы вы ни обращались к регистрации метода (express.get и т. Д.), Вам нужно иметь что-то, что берет ссылку на класс, сохраняет его в реестре (буквально просто массив в каком-то загрузочном файле, аналогично модулям Angular. ) и регистрирует все в этом файле загрузки / модуля.
Откройте свойство __module
, прочитайте информацию, которую вы сохранили, и зарегистрируйте ее при необходимости. Таким образом, вы выполняете разделение задач, у вас есть четкое понимание того, что создается в вашем приложении, и вы можете использовать свои декораторы именно так, как считаете нужным.