Skip to content

Instantly share code, notes, and snippets.

@rjdmacedo
Last active April 24, 2019 13:06
Show Gist options
  • Save rjdmacedo/4231bd879542f41bde6c04f85a5c5316 to your computer and use it in GitHub Desktop.
Save rjdmacedo/4231bd879542f41bde6c04f85a5c5316 to your computer and use it in GitHub Desktop.
interface Consumer<T> {
(...data: T[]): void
}
type Listener<T> = Consumer<T>
export interface Emittable<T> {
/**
* Removes all listeners and all events.
*/
removeAllListeners(): void
/**
* Broadcasts to a certain event and provides a payload to their listeners.
* @param event Event name
* @param data Payload to emit to the listeners
*/
emit<K extends keyof T>(event: K, data: T[K] extends undefined | null ? undefined : T[K]): void
/**
* Adds a listener to an event. When some payload gets a payload emitted, the listener is removed
* @param event Event name
* @param listener Listener Consumer function
*/
once<K extends keyof T>(event: K, listener: Listener<T[K]>): void
/**
* Adds a listener to an event.
* @param event Event name
* @param listener Listener Consumer function
*/
on<K extends keyof T>(event: K, listener: Listener<T[K]>): () => void
/**
* Removes a particular listener from a particular event.
* @param event Event name
* @param listener Listener Consumer function
*/
removeListener<K extends keyof T>(event: K, listener: Listener<T[K]>): void
}
abstract class Emitter<T> implements Emittable<T> {
abstract emit<K extends keyof T>(event: K, data: T[K] extends undefined | null ? undefined : T[K]): void
abstract on<K extends keyof T>(event: K, listener: Listener<T[K]>): () => void
abstract once<K extends keyof T>(event: K, listener: Listener<T[K]>): void
abstract removeAllListeners(): void
abstract removeListener<K extends keyof T>(event: K, listener: Listener<T[K]>): void
}
type Event<Base> = {
[Key in keyof Base]: Listener<Base[Key]>[]
} | {}
import { isArray } from 'lodash'
import { isArray } from 'lodash'
import { Emitter } from '@helpers/index'
import { Event, Listener } from '@type/index'
import { isArray } from 'lodash'
import { Emitter } from '@helpers/index'
import { Event, Listener } from '@type/index'
export class EventEmitter<T> extends Emitter<T> {
protected events: Event<T> = {}
public constructor() {
super()
}
public on<K extends keyof T>(event: K, listener: Listener<T[K]>): () => void {
if (!isArray(this.events[<string>event])) {
this.events[<string>event] = []
}
this.events[<string>event].push(listener)
return () => this.removeListener(event, listener)
}
public removeListener<K extends keyof T>(event: K, listener: Listener<T[K]>): void {
if (typeof this.events[<string>event] !== 'object') return
const idx: number = this.events[<string>event].indexOf(listener)
if (idx > -1) this.events[<string>event].splice(idx, 1)
}
public removeAllListeners(): void {
/**
* @see https://github.com/Microsoft/TypeScript/pull/12253#issuecomment-263132208
*/
Object.keys(this.events).forEach((event: string) => {
this.events[event].splice(0, this.events[event].length)
})
}
public emit<K extends keyof T>(event: K, data: T[K] extends undefined | null ? undefined : T[K]): void {
if (typeof this.events[<string>event] !== 'object') return
this.events[<string>event].forEach((listener: Listener<T[K]>) => listener.call(this, data))
}
public once<K extends keyof T>(event: K, listener: Listener<T[K]>): void {
const removeFromQueue = this.on(event, (args: T[K]) => {
listener.call(this, <T[K]>args)
removeFromQueue()
})
}
}
@rjdmacedo
Copy link
Author

rjdmacedo commented Apr 24, 2019

Example

type TestEvent = {
  'event-1': {
    key?: string
  }
  'event-2': {
    name: string
    private?: boolean
  }
  'event-3': null
}

const emitter = new EventEmitter<TestEvent>()
emitter.on('event-1', data =>
  console.log(`Event key: ${data.key ? data.key : 'Warn: No key was provided.'}`)
)

emitter.on('event-2', data => {
  console.log(
    `Event name: ${data.name}, private: ${
      typeof data.private !== 'undefined' ? data.private : true
    }`
  )
})

emitter.on('event-3', data => console.log(data))

// with key
emitter.emit('event-1', {
  key: '12345'
})

// without key
emitter.emit('event-1', {})

// providing a 'private' property
emitter.emit('event-2', {
  name: 'Coachella',
  private: false
})

// without providing a 'private' property
emitter.emit('event-2', {
  name: 'Dinner with friends'
})

emitter.emit('event-3', undefined)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment