I am currently working with TypeScript code that involves listening to events based on topics generated from specific contexts. I am looking to streamline the event registration process by utilizing method decorators.
For instance, I have a "Controller" class that provides a context for topic generation in event registration and an "@action" decorator that takes this context and returns a topic.
interface ContextProvider<T> {
getContext(): T;
}
type MyContext = {
some_id: number;
};
class Controller implements ContextProvider<MyContext> {
@action(ctx => `event:${ctx.some_id}`)
public eventHandler(message: any) {
console.log("My event handler", message);
}
getContext(): MyContext {
return { some_id: 6 };
}
}
I have created a simple decorator -
function action<TContext>(handler: (TContext) => void): Function {
return function (object: object, aub, descriptor: any) {
if(!object.hasOwnProperty("getContext")) {
throw new Error("Can't bind @action to a class that does not implement the 'getContext' method");
}
// @ts-ignore
const ctx = object.getContext();
console.log(`listening to: ${handler(ctx)}`)
};
}
While it currently works, my concern lies with maintaining type-safety -
ctx
inside the handler is of type "any" instead ofMyContext
- although I have a unit test to verify the correct topic generation, I am seeking better auto-completion and compile-time error detection.- I can mistakenly use the
@action
decorator on a class that does not implement theContextProvider
interface which results in runtime errors; I aim to catch such issues at compile time.
Upon observation, it appears there is no straightforward solution to these problems, leading me to consider resorting to repeated code like:
@action<MyContext>(ctx => `hello:${ctx.some_id}`)
Do you have any suggestions on how to address these challenges?