Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: specialized signatures for EventEmitter subclass events

I have a base class EventEmitter, which has the on method to bind handlers on specific events:

class EventEmitter {
    on(event: string, handler: Function) {
        /* add handler internally */
    }

    protected emit(event: string, ...args) {
        /* call all handlers listening on event */
    }
}

Now I have various subclasses, which can emit different events with different arguments. I'd like to "declare" which events can be emitted by that specific class:

class MyClass extends EventEmitter {
    on(event: 'event1', handler: (arg1: number) => void): void;
    on(event: 'event2', handler: (arg1: string, arg2: number) => void): void;
}

So my sub class may emit the events event1 and event2, but this does not seem to be the right way to specify. TypeScript (tsc 1.8) is compaining:

error TS2415: Class 'MyClass' incorrectly extends base class 'EventEmitter'.
  Types of property 'on' are incompatible.
Type '(event: "event1", handler: (arg1: number) => void) => void' is not assignable to type '(event: string, handler: Function) => void'.
  Type '(event: "event1", handler: (arg1: number) => void) => void' provides no match for the signature '(event: string, handler: Function): void'
error TS2382: Specialized overload signature is not assignable to any non-specialized signature.
error TS2391: Function implementation is missing or not immediately following the declaration.

So, what is the intended way to specify the events my class can emit?

EDIT: I've found the name of what I was looking for: Specialized Signatures. However, it seems to be meant for interfaces only, and not for new TypeScript code.

Now I've found anothother question about the same issue from 2015, however the solution there does not look quite right. So, are there other ways to do it in TypeScript today?

like image 526
Simon Avatar asked Mar 01 '16 23:03

Simon


1 Answers

what is the intended way to specify the events my class can emit?

Instead of using a single event stream that contains all the types its easier to have a separate event stream for each type.

This is a concept I call TypedEvent. One example project that uses it is http://alm.tools/

Implementation : https://github.com/alm-tools/alm/blob/55a8eb0f8ee411a506572abce92085235658b980/src/common/events.ts#L20-L72

Here is an example usage : https://github.com/alm-tools/alm/blob/55a8eb0f8ee411a506572abce92085235658b980/src/server/lang/errorsCache.ts#L10

export let errorsUpdated = new TypedEvent<ErrorsUpdate>();
// emit: 
errorsUpdated.emit({} /* this will be type checked */);
// consume:
errorsUpdated.on((x)=>null); // x has the correct inferred type
like image 137
basarat Avatar answered Nov 13 '22 08:11

basarat