类型安全的 Event Emitter

通常来说,在 Node.js 与传统的 JavaScript 里,你有一个单一的 Event Emitter,你可以用它来为不同的事件添加监听器。

  1. const emitter = new EventEmitter();
  2. // Emit
  3. emitter.emit('foo', foo);
  4. emitter.emit('bar', bar);
  5. // Listen
  6. emitter.on('foo', foo => console.log(foo));
  7. emitter.on('bar', foo => console.log(bar));

实际上,在 EventEmitter 内部以映射数组的形式存储数据:

  1. { foo: [fooListeners], bar: [barListeners] }

为了事件的类型安全,你可以为每个事件类型创建一个 emitter:

  1. const onFoo = new TypedEvent<Foo>();
  2. const onBar = new TypedEvent<Bar>();
  3. // Emit:
  4. onFoo.emit(foo);
  5. onBar.emit(bar);
  6. // Listen:
  7. onFoo.on(foo => console.log(foo));
  8. onBar.on(bar => console.log(bar));

它一些优点:

  • 事件的类型,能以变量的形式被发现。
  • Event Emitter 非常容易被重构。
  • 事件数据结构是类型安全的。

参考 TypedEvent

  1. export interface Listener<T> {
  2. (event: T): any;
  3. }
  4. export interface Disposable {
  5. dispose(): any;
  6. }
  7. export class TypedEvent<T> {
  8. private listeners: Listener<T>[] = [];
  9. private listenersOncer: Listener<T>[] = [];
  10. public on = (listener: Listener<T>): Disposable => {
  11. this.listeners.push(listener);
  12. return {
  13. dispose: () => this.off(listener)
  14. };
  15. };
  16. public once = (listener: Listener<T>): void => {
  17. this.listenersOncer.push(listener);
  18. };
  19. public off = (listener: Listener<T>) => {
  20. const callbackIndex = this.listeners.indexOf(listener);
  21. if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
  22. };
  23. public emit = (event: T) => {
  24. this.listeners.forEach(listener => listener(event));
  25. this.listenersOncer.forEach(listener => listener(event));
  26. this.listenersOncer = [];
  27. };
  28. public pipe = (te: TypedEvent<T>): Disposable => {
  29. return this.on(e => te.emit(e));
  30. };
  31. }

原文: https://jkchao.github.io/typescript-book-chinese/tips/typesafeEventEmitter.html