I am trying to write a TypeScript class that functions as a Function/'Task' registry, such that it is simply a class with a Record<string, Function> static member within that stores that functions that are retrieved simply by the name of the function.
Here is my implementation:
export function RegisterTaskWith(taskRegistry: TaskRegistry) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
taskRegistry.registerTask(propertyKey, descriptor.value);
taskRegistry.listTasks();
};
}
export class TaskRegistry {
private tasks: Record<string, () => Promise<void>> = {};
registerTask(name: string, task: () => Promise<void>) {
this.tasks[name] = task;
}
async executeTask(name: string) {
const task = this.tasks[name];
if (task) {
await task();
} else {
throw new Error(`Task "${name}" not found in the registry.`);
}
}
listTasks() {
return Object.keys(this.tasks);
}
}
const taskRegistry = new TaskRegistry();
class MyTasks {
registeredTasks: string[] = [];
@RegisterTaskWith(taskRegistry)
async task1() {
this.registeredTasks.push('task1'); # Line 1
console.log('Task 1 executed.');
}
@RegisterTaskWith(taskRegistry)
async task2() {
this.registeredTasks.push('task2'); # Line 2
console.log('Task 2 executed.');
}
}
const myTasks = new MyTasks();
console.log('Registered Tasks:', taskRegistry.listTasks());
taskRegistry.executeTask('task1');
// this.registeredTasks.push('task1');
^
//TypeError: Cannot read properties of undefined (reading 'registeredTasks')
taskRegistry.executeTask('task2');
console.log("myTasks.registeredTasks: ", myTasks.registeredTasks)
// The main problem is that the `this` in Line 1 and Line 2 is not an instance of `MyTasks`.
The error that I encountered this is the this on Line 1 and Line 2 does not refer to the instance of MyTasks hence they have no access to MyTasks.registeredTasks member.
How can I make sure that an instance of it is also passed into the registry so that I can call the function by retrieving it from the registry and simply call it?
Playground (although it doesn't show the error but registeredTasks is empty at the end which shouldn't be the case).
When the
RegisterTaskWithdecorator calls theregisterTaskmethod, it passes in thedescriptor.value(the method the decorator is attched to) as an argument. The function however doesn't keep it's context (more info about JS context from MDN here). To keep the context, you would need to bind the function theMyTasksinstance again. That is not possible, since you cannot access the class instance from the method decorator.Just as a note - I'm not really sure what you are trying to achieve, but using a class method with a different context is not common practise in TypeScript so the decorators aren't really made with this in mind. I would consider passing working with the whole class instance, as oposed to the individual methods.
So to answer your question directly: you simply cannot pass the instance to the registry using a decorator.
If you really need the beahviour you are describing, I would consider adding the tasks in the
MyTasksconstrutor something like this: