Usage Patterns
Compose emitter instances
ts
const emitterA = new EventEmitter<{ ready: [void] }>()
const emitterB = new EventEmitter<{ ready: [void] }>()
const bridge = (event: 'ready') => emitterA.on(event, (...args) => emitterB.emit(event, ...args))
bridge('ready')Use this to forward events between modules or to expose a single emitter API backed by multiple internal sources.
Integrate with DOM APIs
ts
const ui = new EventEmitter<{ click: [MouseEvent], close: [void] }>()
document.addEventListener('click', (evt) => ui.emit('click', evt))
document.querySelector('[data-close]')?.addEventListener('click', () => ui.emit('close'))Scoped teardown
ts
const emitter = new EventEmitter<{ change: [string] }>()
const listeners = [
() => emitter.on('change', (key) => console.log('first', key)),
() => emitter.once('change', (key) => console.log('second', key)),
]
export function mount() {
listeners.forEach((register) => register())
}
export function unmount() {
emitter.clearListeners()
}Async workflows
ts
const emitter = new EventEmitter<{ done: [string], error: [Error] }>()
export const runTask = async (task: () => Promise<string>) => {
try {
const result = await task()
emitter.emit('done', result)
} catch (err) {
emitter.emit('error', err as Error)
}
}Namespacing events
ts
type Namespaced<T extends string> = `${T}:${string}`
const emitter = new EventEmitter<Record<Namespaced<'graph' | 'ui'>, [unknown]>>()
type GraphEvent = `graph:${string}`
emitter.on('graph:ready', () => ...)More real-world scenarios? Extend this file with contributions that show how to bridge frameworks, log listener counts, or debug stuck events.