Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collecting decorated classes (without registry classes)

Tags:

typescript

I've got an event dispatcher class which should trigger listeners on certain occurrences. I want to register event listeners via decorators - which already works but I'm not a 100% satisfied with my current solution.

Currently event listeners are "collected" by a global registries and later registered in the event dispatcher. Speaking in code my (simplified) decorator looks like:

// Decorator
export const EventListener = (events: string[]) => {
  return (target: Type<object>): void => {
    EventListenerRegistry.set(target);
  };
};

// Listener example
@EventListener(['start'])
class OnStart {
  public handle() {
  }
}

The OnStart listener would be triggered as soon as the start event is dispatched.

What I don't like about this approach is the need of the EventListenerRegistry which looks like:

export const EventListenerRegistry = (new class {
    protected listeners = [];

    public set(target) {
        this.listeners.push(target);
    }
});

Importing the EventListenerRegistry means importing the same instance at any point, making it pretty "singleton'ish".

My listeners are required via glob - so this approach makes it possible to automatically register my listeners to my dispatcher without having to do any additional work.

But is there any other possibility than using (ugly) registry classes?

like image 914
nehalist Avatar asked May 31 '26 09:05

nehalist


1 Answers

I would use an IoC container. A nice one for TypeScript is Inversify. This would allow something like this:

import { injectable, inject } from 'inversify';

enum EventType {
  Start,
}

interface IEvent {
  type: Event;
  handle(): void;
}

@injectable()
class OnStart implements IEvent {
  public readonly type = EventType.Start;
  public handle() {
  }
}

@injectable()
class Application {
  public constructor(@multiInject(EVENT) public events: IEvent[]) {
  }
}

But this does mean you have to do the binding yourself:

export const EVENT = symbol('event');
container.bind<IEvent>(EVENT).to(OnStart);
...

However, if you really want to let the events auto-register themselves, I guess you can simply import the container into your decorators and do the binding there.

like image 106
sroes Avatar answered Jun 03 '26 00:06

sroes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!