Typesafe Event Emitter
Conventionally in Node.js and traditional JavaScript you have a single event emitter. This event emitter internally tracks listener for different event types e.g.
const emitter = new EventEmitter();
// Emit: 
emitter.emit('foo', foo);
emitter.emit('bar', bar);
// Listen: 
emitter.on('foo', (foo)=>console.log(foo));
emitter.on('bar', (bar)=>console.log(bar));
Essentially EventEmitter internally stores data in the form of mapped arrays: 
{foo: [fooListeners], bar: [barListeners]}
Instead, for the sake of event type safety, you can create an emitter per event type:
const onFoo = new TypedEvent<Foo>();
const onBar = new TypedEvent<Bar>();
// Emit: 
onFoo.emit(foo);
onBar.emit(bar);
// Listen: 
onFoo.on((foo)=>console.log(foo));
onBar.on((bar)=>console.log(bar));
This has the following advantages:
- The types of events are easily discoverable as variables.
- The event emitter variables are easily refactored independently.
- Type safety for event data structures.
Reference TypedEvent
export interface Listener<T> {
  (event: T): any;
}
export interface Disposable {
  dispose();
}
/** passes through events as they happen. You will not get events from before you start listening */
export class TypedEvent<T> {
  private listeners: Listener<T>[] = [];
  private listenersOncer: Listener<T>[] = [];
  on = (listener: Listener<T>): Disposable => {
    this.listeners.push(listener);
    return {
      dispose: () => this.off(listener)
    };
  }
  once = (listener: Listener<T>): void => {
    this.listenersOncer.push(listener);
  }
  off = (listener: Listener<T>) => {
    var callbackIndex = this.listeners.indexOf(listener);
    if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
  }
  emit = (event: T) => {
    /** Update any general listeners */
    this.listeners.forEach((listener) => listener(event));
    /** Clear the `once` queue */
    if (this.listenersOncer.length > 0) {
      const toCall = this.listenersOncer;
      this.listenersOncer = [];
      toCall.forEach((listener) => listener(event));
    }
  }
  pipe = (te: TypedEvent<T>): Disposable => {
    return this.on((e) => te.emit(e));
  }
}