bastienrobert/events

View on GitHub
src/index.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { filter } from './helpers'
import { Event, Events } from './types'

export type Arguments = any[]
export type Callback = (...args: Arguments) => any

export default class EventEmitter {
  private _events: Events = {}

  /**
   * Returns an array listing the events for which the emitter has registered listeners
   */
  public get eventNames(): string[] {
    return Object.keys(this._events)
  }

  /**
   * Returns a copy of the array of listeners for the event named eventName
   * @param name - name of the event
   */
  public listeners(name: string): Event[] | [] {
    const event = this._events[name]
    return event ? event.slice() : []
  }

  /**
   * Returns the number of listeners listening to the given event name
   * @param name - name of the event
   * @returns number of listeners listening to the given event name
   */
  public listenerCount(name: string): number {
    const event = this._events[name]
    return event ? event.length : 0
  }

  /**
   * Alias for emitter.on(name, fn)
   */
  public addListener = this.on

  /**
   * Alias for emitter.off(name, fn)
   */
  public removeListener = this.off

  /**
   * Adds the listener function to the end of the listeners array for the given event name
   * @param name - name of the event
   * @param fn - callback function
   * @returns EventEmitter instance
   */
  public on(name: string, fn: Callback): this {
    if (!this._events[name]) this._events[name] = []
    this._events[name].push(fn)
    return this
  }

  /**
   * Adds a one-time listener function for the given event name
   * @param name - name of the event
   * @param fn - callback function
   * @returns EventEmitter instance
   */
  public once(name: string, fn: Callback): this {
    const onceFn = (...args: any[]) => {
      fn.apply(this, args)
      this.off(name, onceFn)
    }
    this.on(name, onceFn)
    return this
  }

  /**
   * Removes the specified listener from the listener array for the given event name
   * @param name - name of the event
   * @param fn - callback function
   * @returns EventEmitter instance
   */
  public off(name: string, fn: Callback): this {
    if (this._events[name]) {
      const result = filter(this._events[name], (event) => event !== fn)
      if (result.length > 0) this._events[name] = result
      else delete this._events[name]
    }
    return this
  }

  /**
   * Removes all listeners, or those of the specified eventName
   *
   * **It's a bad practice to remove listeners added elsewhere in the code**
   * @param name - name of the event
   * @returns EventEmitter instance
   */
  public removeAllListeners(name?: string): this {
    if (!name) this.eventNames.forEach((n) => this.removeAllListeners(n))
    else if (this._events[name]) delete this._events[name]
    return this
  }

  /**
   * Synchronously calls each of the listeners registered for the event named eventName, in the order they were registered, passing the supplied arguments to each
   * @param name - name of the event
   * @param ...args - supplied arguments to listeners
   * @returns returns true if the event had listeners, false otherwise
   */
  public emit(name: string, ...args: Arguments): boolean {
    if (!this._events[name]) return false

    const length = this._events[name].length
    for (let i = 0; i < length; i++) {
      this._events[name][i].apply(null, args)
    }
    return length > 0
  }
}