Declaring events in a TypeScript class which extends EventEmitter

前端 未结 4 1481
有刺的猬
有刺的猬 2021-01-01 11:20

I have a class extends EventEmitter that can emit event hello. How can I declare the on method with specific event name and listener s

相关标签:
4条回答
  • 2021-01-01 11:33

    You can use typed event emitter package for this.

    eg:

    import { EventEmitter } from 'tsee';
    
    const events = new EventEmitter<{
        foo: (a: number, b: string) => void,
    }>();
    
    // foo's arguments is fully type checked
    events.emit('foo', 123, 'hello world');
    

    This package also provide interfaces & some utils.

    0 讨论(0)
  • 2021-01-01 11:51

    to extend @SergeyK's answer, with this you can get type-checking and completion on both emit and on functions without repeating event types.

    1. Define event listener signatures for each event type:
    interface MyClassEvents {
      'add': (el: string, wasNew: boolean) => void;
      'delete': (changedCount: number) => void;
    }
    
    1. Declare interface which constructs types for MyClass, based on EventListeners (MyClassEvents) function signature:
    declare interface MyClass {
      on<U extends keyof MyClassEvents>(
        event: U, listener: MyClassEvents[U]
      ): this;
    
      emit<U extends keyof MyClassEvents>(
        event: U, ...args: Parameters<MyClassEvents[U]>
      ): boolean;
    }
    
    1. Simply define you class extending EventEmitter:
    class MyClass extends EventEmitter {
      constructor() {
        super();
      }
    }
    

    Now you will get type checking for on and emit functions:

    Unfortunately you will get completion and type-checking only on those two functions (unless you define more functions inside MyClass interface).

    To get more generic solution, you can use this package. note: it adds no runtime overhead.

    import { TypedEmitter } from 'tiny-typed-emitter';
    
    interface MyClassEvents {
      'add': (el: string, wasNew: boolean) => void;
      'delete': (changedCount: number) => void;
    }
    
    class MyClass extends TypedEmitter<MyClassEvents> {
      constructor() {
        super();
      }
    }
    
    0 讨论(0)
  • 2021-01-01 11:54

    Most usable way of doing this, is to use declare:

    declare interface MyClass {
        on(event: 'hello', listener: (name: string) => void): this;
        on(event: string, listener: Function): this;
    }
    
    class MyClass extends events.EventEmitter {
        emitHello(name: string): void {
            this.emit('hello', name);
        }
    }
    

    Note that if you are exporting your class, both the interface and class have to be declared with the export keyword.

    0 讨论(0)
  • 2021-01-01 11:54

    Here's what I was able to figure out. Overriding the default function with a generic!

    interface IEmissions {
      connect: () => void
      test: (property: string) => void
    }
    
    class MyClass extends events.EventEmitter {
      private _untypedOn = this.on
      private _untypedEmit = this.emit
      public on = <K extends keyof IEmissions>(event: K, listener: IEmissions[K]): this => this._untypedOn(event, listener)
      public emit = <K extends keyof IEmissions>(event: K, ...args: Parameters<IEmissions[K]>): boolean => this._untypedEmit(event, ...args)
    
      this.emit('test', 'Testing') // This will be typed for you!
    }
    
    // Example:
    const inst = new MyClass()
    inst.on('test', info => console.log(info)) // This will be typed!
    
    0 讨论(0)
提交回复
热议问题