Last active
April 9, 2024 05:43
-
-
Save rusintez/1b9af057eb4160fe1e8d28784d099238 to your computer and use it in GitHub Desktop.
Typescript Event Emitter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Strongly typed event emitter | |
* | |
* @usage | |
* | |
* const emitter = createEmitter<{ | |
* signature: [{ publicKey: number[]; signature: number[] }]; | |
* }>(); | |
* | |
* emitter.on("signature", ({ publicKey, signature }) => { | |
* console.log({ publicKey, signature }); | |
* }); | |
*/ | |
export type IEventMap = Record<string, any[]>; | |
export type IEventNames<Events extends IEventMap> = Exclude< | |
keyof Events, | |
number | symbol | |
>; | |
export type IEventParams< | |
Events extends IEventMap, | |
Name extends IEventNames<Events> | |
> = Events[Name]; | |
export type IEventHandler< | |
Events extends IEventMap, | |
Name extends IEventNames<Events> | |
> = (...args: IEventParams<Events, Name>) => unknown; | |
type IOnce = boolean; | |
export type IEventEmitter<IEvents extends IEventMap> = { | |
on< | |
Name extends IEventNames<IEvents>, | |
Handler extends IEventHandler<IEvents, Name> | |
>( | |
name: Name, | |
handler: Handler | |
): IEventEmitter<IEvents>; | |
once< | |
Name extends IEventNames<IEvents>, | |
Handler extends IEventHandler<IEvents, Name> | |
>( | |
name: Name, | |
handler: Handler | |
): IEventEmitter<IEvents>; | |
off< | |
Name extends IEventNames<IEvents>, | |
Handler extends IEventHandler<IEvents, Name> | |
>( | |
name?: Name, | |
handler?: Handler | |
): IEventEmitter<IEvents>; | |
emit< | |
Name extends IEventNames<IEvents>, | |
Params extends IEventParams<IEvents, Name> | |
>( | |
name: Name, | |
...args: Params | |
): IEventEmitter<IEvents>; | |
}; | |
export const createEmitter = < | |
Events extends IEventMap | |
>(): IEventEmitter<Events> => { | |
type EventNames = keyof Events; | |
type EventParams<Name extends EventNames> = Events[Name]; | |
type EventHandler<EventName extends EventNames> = ( | |
...args: EventParams<EventName> | |
) => unknown; | |
let listeners = new Map<EventNames, [EventHandler<any>, IOnce][]>(); | |
const emitter: IEventEmitter<Events> = { | |
on(name, handler) { | |
if (listeners.has(name)) listeners.get(name)?.push([handler, false]); | |
else listeners.set(name, [[handler, false]]); | |
return emitter; | |
}, | |
once(name, handler) { | |
if (listeners.has(name)) listeners.get(name)?.push([handler, false]); | |
else listeners.set(name, [[handler, false]]); | |
return emitter; | |
}, | |
off(name, handler) { | |
if (!name) { | |
listeners = new Map<EventNames, [EventHandler<any>, IOnce][]>(); | |
return emitter; | |
} | |
if (listeners.has(name)) { | |
if (handler) { | |
let pairs = listeners.get(name)!; | |
pairs = pairs.filter(([func]) => func === handler); | |
if (pairs.length) listeners.set(name, pairs); | |
else listeners.delete(name); | |
} else { | |
listeners.delete(name); | |
} | |
} | |
return emitter; | |
}, | |
emit(name, ...args) { | |
let pairs = listeners.get(name); | |
if (pairs) { | |
pairs = pairs.filter(([handle, emitOnce]) => { | |
handle(...args); | |
return emitOnce === false; | |
}); | |
if (pairs.length) listeners.set(name, pairs); | |
else listeners.delete(name); | |
} | |
return emitter; | |
}, | |
}; | |
return emitter; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment