ee-ts
Type-safe event emitters (for TypeScript)
Features
- strict event names
- type-checking for emitted data
- flexible
listeners()
generator method - add/remove listeners during emit
- great for sub-classing
- one-time listeners
- default handlers
Example
import { EventEmitter as EE } from 'ee-ts'
type User = { name: string }
// All possible events must be explicitly defined as methods here.
// The return type can be non-void because the `emit` method returns the last non-void value.
// The return type can never be required, because `void` is implicitly added to every event.
interface Events {
login(user: User): void
logout(): string
}
// Make your subclass generic to let users add their own events.
class App<T = {}> extends EE<T & Events> {
// You _cannot_ emit user-added events from here, though.
someMethod(this: App) {
this.emit('logout')
}
}
type UserEvents = { test(): void }
let app = new App<UserEvents>()
// Emit your custom event.
app.emit('test')
// The type of `user` is inferred.
app.on('login', user => {
console.log(user.name) // user.name is string
})
// Invalid argument types are caught.
app.one('login', (invalid: boolean) => {}) // [ts] Type 'User' is not assignable to type 'boolean'.
// Invalid return values are caught.
app.one('logout', () => true) // [ts] Type 'boolean' is not assignable to type 'string | void'.
// Unknown event names are caught.
app.emit('invalid') // [ts] Argument of type '"invalid"' is not assignable to parameter of type '"login" | "logout"'.
Subclassing
This library was designed with subclassing in mind.
- The internal cache is non-enumerable
- Few public methods:
on
,one
,off
,emit
,listeners
- Override
_onEventHandled(key: string)
to know when an event has at least one listener - Override
_onEventUnhandled(key: string)
to know when an event has no listeners
Disposables
When you pass an array as the last argument of on
, one
, or EE.unhandle
,
an object is pushed onto it. This object has a dispose(): void
method, which
you should call to remove the associated listener from its event.
This is a useful way of grouping listeners together.
import { EventEmitter, Disposable } from 'ee-ts'
const ee = new EventEmitter<{ foo(): void }>()
const disposables: Disposable[] = []
let count = 0
const fn = ee.on('foo', () => count++, disposables)
assert(disposables.length == 1)
disposables[0].dispose()
ee.emit('foo')
assert(count == 0)
API Reference
The type signatures below are not 100% accurate. They're here to give you a general idea of the API. Find the real type signatures in the source code or VS Code.
on(key: string, fn: Function, disposables?: Disposable[]): Function
Add a listener to the given event key.
Use the one
method to add a one-time listener.
Returns: the fn
argument
on(map: { [key: string]: Function }, disposables?: Disposable[]): this
Add every listener value to its associated event key.
Use the one
method to add one-time listeners.
off(key: string, fn?: Function): this
Remove a listener for the given event key.
Omit the fn
argument to remove all listeners for the given event key.
Call off('*')
to remove all listeners for every event key.
emit(key: string, ...args: any[]): any
Emit an event to listeners associated with the given event key.
You can safely add/remove listeners from inside a listener.
Returns: last non-void value returned by a listener
listeners(key: string): IterableIterator<Function>
Create a generator of the listeners for the given event key.
Use this with for..of
or spread it into an array. Read more about generators here.
Static methods
unhandle(ee: EventEmitter, key: string, fn: Function, disposables?: Disposable[]): Function
Set the default handler for an event key.
The default handler is called when no other listeners exist for the same event key.
keys(ee: EventEmitter): string[]
Get an array of event keys that have listeners.
count(ee: EventEmitter, key: string): number
Get the number of listeners an event has.
has(ee: EventEmitter, key: string): boolean
Check if an event has listeners.
Returns: true when the given event key has >= 1
listener.