/** Emittery accepts strings and symbols as event names. Symbol event names can be used to avoid name collisions when your classes are extended, especially for internal events. */ type EventName = string | symbol; /** Emittery also accepts an array of strings and symbols as event names. */ type EventNames = EventName | readonly EventName[]; declare class Emittery { /** In TypeScript, it returns a decorator which mixins `Emittery` as property `emitteryPropertyName` and `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the target class. @example ``` import Emittery = require('emittery'); @Emittery.mixin('emittery') class MyClass {} const instance = new MyClass(); instance.emit('event'); ``` */ static mixin(emitteryPropertyName: string | symbol, methodNames?: readonly string[]): Function; /** Fires when an event listener was added. An object with `listener` and `eventName` (if `on` or `off` was used) is provided as event data. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); emitter.on(Emittery.listenerAdded, ({listener, eventName}) => { console.log(listener); //=> data => {} console.log(eventName); //=> '🦄' }); emitter.on('🦄', data => { // Handle data }); ``` */ static readonly listenerAdded: unique symbol; /** Fires when an event listener was removed. An object with `listener` and `eventName` (if `on` or `off` was used) is provided as event data. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); const off = emitter.on('🦄', data => { // Handle data }); emitter.on(Emittery.listenerRemoved, ({listener, eventName}) => { console.log(listener); //=> data => {} console.log(eventName); //=> '🦄' }); off(); ``` */ static readonly listenerRemoved: unique symbol; /** Subscribe to one or more events. Using the same listener multiple times for the same event will result in only one method call per emitted event. @returns An unsubscribe method. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); emitter.on('🦄', data => { console.log(data); }); emitter.on(['🦄', '🐶'], data => { console.log(data); }); emitter.emit('🦄', '🌈'); // log => '🌈' x2 emitter.emit('🐶', '🍖'); // log => '🍖' ``` */ on(eventName: typeof Emittery.listenerAdded | typeof Emittery.listenerRemoved, listener: (eventData: Emittery.ListenerChangedData) => void): Emittery.UnsubscribeFn on(eventName: EventNames, listener: (eventData?: unknown) => void): Emittery.UnsubscribeFn; /** Get an async iterator which buffers data each time an event is emitted. Call `return()` on the iterator to remove the subscription. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); const iterator = emitter.events('🦄'); emitter.emit('🦄', '🌈1'); // Buffered emitter.emit('🦄', '🌈2'); // Buffered iterator .next() .then(({value, done}) => { // done === false // value === '🌈1' return iterator.next(); }) .then(({value, done}) => { // done === false // value === '🌈2' // Revoke subscription return iterator.return(); }) .then(({done}) => { // done === true }); ``` In practice you would usually consume the events using the [for await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) statement. In that case, to revoke the subscription simply break the loop. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); const iterator = emitter.events('🦄'); emitter.emit('🦄', '🌈1'); // Buffered emitter.emit('🦄', '🌈2'); // Buffered // In an async context. for await (const data of iterator) { if (data === '🌈2') { break; // Revoke the subscription when we see the value `🌈2`. } } ``` It accepts multiple event names. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); const iterator = emitter.events(['🦄', '🦊']); emitter.emit('🦄', '🌈1'); // Buffered emitter.emit('🦊', '🌈2'); // Buffered iterator .next() .then(({value, done}) => { // done === false // value === '🌈1' return iterator.next(); }) .then(({value, done}) => { // done === false // value === '🌈2' // Revoke subscription return iterator.return(); }) .then(({done}) => { // done === true }); ``` */ events(eventName: EventNames): AsyncIterableIterator /** Remove one or more event subscriptions. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); const listener = data => console.log(data); (async () => { emitter.on(['🦄', '🐶', '🦊'], listener); await emitter.emit('🦄', 'a'); await emitter.emit('🐶', 'b'); await emitter.emit('🦊', 'c'); emitter.off('🦄', listener); emitter.off(['🐶', '🦊'], listener); await emitter.emit('🦄', 'a'); // nothing happens await emitter.emit('🐶', 'b'); // nothing happens await emitter.emit('🦊', 'c'); // nothing happens })(); ``` */ off(eventName: EventNames, listener: (eventData?: unknown) => void): void; /** Subscribe to one or more events only once. It will be unsubscribed after the first event. @returns The event data when `eventName` is emitted. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); emitter.once('🦄').then(data => { console.log(data); //=> '🌈' }); emitter.once(['🦄', '🐶']).then(data => { console.log(data); }); emitter.emit('🦄', '🌈'); // Logs `🌈` twice emitter.emit('🐶', '🍖'); // Nothing happens ``` */ once(eventName: typeof Emittery.listenerAdded | typeof Emittery.listenerRemoved): Promise once(eventName: EventNames): Promise; /** Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently. @returns A promise that resolves when all the event listeners are done. *Done* meaning executed if synchronous or resolved when an async/promise-returning function. You usually wouldn't want to wait for this, but you could for example catch possible errors. If any of the listeners throw/reject, the returned promise will be rejected with the error, but the other listeners will not be affected. */ emit(eventName: EventName, eventData?: unknown): Promise; /** Same as `emit()`, but it waits for each listener to resolve before triggering the next one. This can be useful if your events depend on each other. Although ideally they should not. Prefer `emit()` whenever possible. If any of the listeners throw/reject, the returned promise will be rejected with the error and the remaining listeners will *not* be called. @returns A promise that resolves when all the event listeners are done. */ emitSerial(eventName: EventName, eventData?: unknown): Promise; /** Subscribe to be notified about any event. @returns A method to unsubscribe. */ onAny(listener: (eventName: EventName, eventData?: unknown) => unknown): Emittery.UnsubscribeFn; /** Get an async iterator which buffers a tuple of an event name and data each time an event is emitted. Call `return()` on the iterator to remove the subscription. In the same way as for `events`, you can subscribe by using the `for await` statement. @example ``` import Emittery = require('emittery'); const emitter = new Emittery(); const iterator = emitter.anyEvent(); emitter.emit('🦄', '🌈1'); // Buffered emitter.emit('🌟', '🌈2'); // Buffered iterator.next() .then(({value, done}) => { // done is false // value is ['🦄', '🌈1'] return iterator.next(); }) .then(({value, done}) => { // done is false // value is ['🌟', '🌈2'] // revoke subscription return iterator.return(); }) .then(({done}) => { // done is true }); ``` */ anyEvent(): AsyncIterableIterator /** Remove an `onAny` subscription. */ offAny(listener: (eventName: EventName, eventData?: unknown) => void): void; /** Clear all event listeners on the instance. If `eventName` is given, only the listeners for that event are cleared. */ clearListeners(eventName?: EventNames): void; /** The number of listeners for the `eventName` or all events if not specified. */ listenerCount(eventName?: EventNames): number; /** Bind the given `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the `target` object. @example ``` import Emittery = require('emittery'); const object = {}; new Emittery().bindMethods(object); object.emit('event'); ``` */ bindMethods(target: object, methodNames?: readonly string[]): void; } declare namespace Emittery { /** Removes an event subscription. */ type UnsubscribeFn = () => void; type EventNameFromDataMap = Extract; /** Maps event names to their emitted data type. */ interface Events { // Blocked by https://github.com/microsoft/TypeScript/issues/1863, should be // `[eventName: EventName]: unknown;` } /** The data provided as `eventData` when listening for `Emittery.listenerAdded` or `Emittery.listenerRemoved`. */ interface ListenerChangedData { /** The listener that was added or removed. */ listener: (eventData?: unknown) => void; /** The name of the event that was added or removed if `.on()` or `.off()` was used, or `undefined` if `.onAny()` or `.offAny()` was used. */ eventName?: EventName; } /** Async event emitter. You must list supported events and the data type they emit, if any. @example ``` import Emittery = require('emittery'); const emitter = new Emittery.Typed<{value: string}, 'open' | 'close'>(); emitter.emit('open'); emitter.emit('value', 'foo\n'); emitter.emit('value', 1); // TS compilation error emitter.emit('end'); // TS compilation error ``` */ class Typed extends Emittery { on>(eventName: Name, listener: (eventData: EventDataMap[Name]) => void): Emittery.UnsubscribeFn; on(eventName: Name, listener: () => void): Emittery.UnsubscribeFn; events>(eventName: Name): AsyncIterableIterator; once>(eventName: Name): Promise; once(eventName: Name): Promise; off>(eventName: Name, listener: (eventData: EventDataMap[Name]) => void): void; off(eventName: Name, listener: () => void): void; onAny(listener: (eventName: EventNameFromDataMap | EmptyEvents, eventData?: EventDataMap[EventNameFromDataMap]) => void): Emittery.UnsubscribeFn; anyEvent(): AsyncIterableIterator<[EventNameFromDataMap, EventDataMap[EventNameFromDataMap]]>; offAny(listener: (eventName: EventNameFromDataMap | EmptyEvents, eventData?: EventDataMap[EventNameFromDataMap]) => void): void; emit>(eventName: Name, eventData: EventDataMap[Name]): Promise; emit(eventName: Name): Promise; emitSerial>(eventName: Name, eventData: EventDataMap[Name]): Promise; emitSerial(eventName: Name): Promise; } } export = Emittery;