Skip to content

Instantly share code, notes, and snippets.

@Paratron
Last active April 22, 2022 13:48
Show Gist options
  • Save Paratron/faacf00bc9a06d8adce1220c6a65f041 to your computer and use it in GitHub Desktop.
Save Paratron/faacf00bc9a06d8adce1220c6a65f041 to your computer and use it in GitHub Desktop.
A typed event publish / subscribe system
/**
* Created by Christian Engel (parastudios.de)
* This code is provided as-is and needs to be used at own risk.
*
* v 1.1
*/
type EventCallbackFunction<EventMap, Key extends keyof EventMap> = (data: EventMap[Key]) => void;
type AllEventCallbackFunction<EventMap> = (event: keyof EventMap, data: EventMap[keyof EventMap]) => void;
export interface EventSystem<EventMap> {
on: <Key extends keyof EventMap>(event: Key, callback: EventCallbackFunction<EventMap, Key>) => void;
off: <Key extends keyof EventMap>(event: Key, callback: EventCallbackFunction<EventMap, Key>) => void;
onAllEvents: (callback: AllEventCallbackFunction<EventMap>) => void;
offAllEvents: (callback: AllEventCallbackFunction<EventMap>) => void;
trigger: <Key extends keyof EventMap = keyof EventMap>(event: Key, ...rest: (EventMap[Key] extends void | undefined ? [data?: undefined] : [data: EventMap[Key]])) => void;
}
/**
* This method creates a typed event system.
* Pass a typescript interface to define your events (interface keys) and their
* data payloads (interface values).
*/
export default function createEventSystem<EventMap>(): EventSystem<EventMap> {
let listeners: {[key in keyof EventMap]?:EventCallbackFunction<EventMap, keyof EventMap>[]} = {};
let allListeners: AllEventCallbackFunction<EventMap>[] = [];
return {
on: (event, callback) => {
listeners[event] = listeners[event] || [];
listeners[event]!.push(callback as EventCallbackFunction<EventMap, keyof EventMap>);
},
onAllEvents: (callback) => {
allListeners.push(callback);
},
off: (event, callback) => {
if(!listeners[event]){
return;
}
const index = listeners[event]!.indexOf(callback as EventCallbackFunction<EventMap, keyof EventMap>);
if(index !== -1){
listeners[event]!.splice(index, 1);
}
},
offAllEvents: (callback) => {
const index = allListeners.indexOf(callback);
if(index !== -1){
allListeners.splice(index, 1);
}
},
trigger: (event, ...rest) => {
const [data] = rest;
if(listeners[event]){
listeners[event]!.forEach(cb => cb(data as EventMap[keyof EventMap]));
allListeners.forEach(cb => cb(event, data as EventMap[keyof EventMap]));
}
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment