Skip to content

Instantly share code, notes, and snippets.

@robinsax
Created May 15, 2025 17:29
Show Gist options
  • Save robinsax/3ba7de346b9031e60f4e91b50ae41af6 to your computer and use it in GitHub Desktop.
Save robinsax/3ba7de346b9031e60f4e91b50ae41af6 to your computer and use it in GitHub Desktop.
Simple TypeScript EventBus
export type Event<T> = {
event: T;
};
export type BaseEvent = Event<string>;
export type GenericCallback<T extends BaseEvent> = (data: Extract<T, { event: T['event'] }>) => void;
export type Callback<T extends BaseEvent, K extends T['event']> = (data: Extract<T, { event: K }>) => void;
export class EventBus<T extends BaseEvent> {
private channel: BroadcastChannel;
private listeners: Map<T['event'], [unknown, GenericCallback<T>][]>;
constructor(name: string) {
this.channel = new BroadcastChannel(name);
this.listeners = new Map();
}
emit<K extends T['event']>(event: K, data: Omit<Extract<T, { event: K }>, 'event'>) {
this.channel.postMessage({ event, data });
}
subscribe<K extends T['event']>(owner: unknown, event: K, callback: Callback<T, K>) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)?.push([owner, callback as unknown as GenericCallback<T>]);
}
unsubscribe(owner: unknown) {
for (const [event, listeners] of this.listeners) {
this.listeners.set(event, listeners.filter(([listenerOwner]) => listenerOwner !== owner));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment