typed-emitter
typed-emitter copied to clipboard
Generics example for 2.x does not compile
The example for generics in the documentation does not work:
class MyEventEmitter<T> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> {
// ...
}
Just gives error TS2344: Type 'T' does not satisfy the constraint 'EventMap'.
on the TypedEmitter<T>
part...
TypeScript 4.5.5
Also tried adding it as a constraint on T, but same happens with class MyEventEmitter<T extends EventMap> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> { .. }
- still the same error
Also tried adding it as a constraint on T, but same happens with class MyEventEmitter<T extends EventMap> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> { .. } - still the same error
That should work, though. Can you provide a minimal sample? @sp00x
well...
Basically just that one line, even if it doesn't make much sense:
import TypedEmitter from 'typed-emitter';
import EventEmitter from 'events';
class MyEventEmitter<T> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> {
// whatever
}
or if you want to use it with your example MessageEvents event type
import TypedEmitter from 'typed-emitter';
import EventEmitter from 'events';
type MessageEvents = {
error: (error: Error) => void,
message: (body: string, from: string) => void
}
class MyEventEmitter<T> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> {
// whatever
}
class SomethingThatEmitsMessages extends MyEventEmitter<MessageEvents> {
// whatever
}
You get an error on the <T>
Now I see. Yeah, that won't work… We need to constrain the generic type parameter T
:
import TypedEmitter from 'typed-emitter';
+ import { EventMap } from 'typed-emitter';
import EventEmitter from 'events';
- class MyEventEmitter<T> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> {
+ class MyEventEmitter<T extends EventMap> extends (EventEmitter as { new<T>(): TypedEmitter<T> })<T> {
// whatever
}
Try and see if it works for you, then let's update the readme :)
Is that not the same as I mentioned in the 2nd comment above, or am I missing something? https://github.com/andywer/typed-emitter/issues/32#issuecomment-1041313893
(If so it gives the same error..)
@sp00x Right… But how can it still say error TS2344: Type 'T' does not satisfy the constraint 'EventMap'.
is there is the type constraint class MyEventEmitter<T extends EventMap>
?
I think this requires a runnable code sample in TS playground / code sandbox or similar.
Hello Developers,
I faced similar problem, and here is the workaround that I found:
export class BaseEmitter<T extends EventMap> extends (EventEmitter as {
new <T>(): TypedEmitter<T & EventMap>;
})<T> {}
Now you can use it like this:
type IAuthCustomerEvents = {
signup: (email: string) => void;
password_reset: (email: string) => void;
};
class AuthCustomerEmitter extends BaseEmitter<IAuthCustomerEvents> {}
@devjayantmalik Nice, then it is actually happy! (In fact, it seems you don't need the <T extends EventMap>
but only the <T & EventMap>
part for TS to be happy)
https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAFQJ5gKYBMCiJgxqqAGjgG85MA3VAOxgFkBDMOAXzgDMoIQ4ByGFBgC0qHHgK8A3AFgAUKEixyVWtlz4oHLj16oVMAM5S5cgWjh1UBgwwDmqSjUNwAvKTlw4BLlABccAApvaH9MKB8ASlcAPjgKCGB0Qg84ECsbe38AgCMIdCR-AxgoYGpbYk5uQuLS2yiXWPjEuRYTWQBjABsGawskR1UxDQAeBFjUAA98anQDQIGYNXFNHtI4alQAd1HogIj-ZDQsIYJRuAAyZSdGMFiWCJ33WU8Aehe4TYALBnwqKBa2l0enMAMrcVAwT61BDfRZiAyWax2KxeKY0WZ9BZLEaIjIOfQGWIkFJvD6wvQEFpAA
There is another way with a constrained type of T
:
class MyEventEmitter<T extends EventMap> extends (EventEmitter as { new<T extends EventMap>(): TypedEmitter<T> })<T> {
/* ^ ^ ^ ^
defining first T defining second T using second T using first T
*/
}
The reason is that T
is redefined without constrain.
Has anyone gotten this to work with potentially multiple sub-classes in a chain? I want to be able to define a class like:
import { EventEmitter } from 'events';
import TypedEmitter, { EventMap } from 'typed-emitter';
export class BaseEmitter<T extends EventMap> extends (EventEmitter as {
new <T>(): TypedEmitter<T & EventMap>;
})<T> {}
type ParentEvents = {
event_one: (arg0: number) => void;
}
// A subclass can extend Parent/ParentEvents to add its own events
class Parent<T extends ParentEvents> extends BaseEmitter<T> {
something() {
this.emit('event_one', 42);
}
}
but I get this error for the line trying to emit the event:
Argument of type '[number]' is not assignable to parameter of type 'Parameters<(T & EventMap)["event_one"]>'
Playground here
I think the problem is that this is possible:
class SubParent extends Parent<{event_one: () => void}> {
something() {
this.emit('event_one', 42); // then this code is invalid
}
}
This looks like a bug but I am not sure. I stopped using typescript because it took more time to get the types right than debugging a typo.
@Sertonix you're saying because, in that scenario, which event_one
is ambiguous? I was wondering if it was related to some ambiguity as well (i.e. since it's some generic T
extending, it doesn't know which)--so I was hoping casting/qualifying the call might help, but I was unable to find the right syntax to get that to work.
I found this is work for me:
import EventEmitter from "events";
import TypedEmitter, { EventMap } from "typed-emitter";
class Animal<T1 extends EventMap = {}> extends (EventEmitter as { new <T2 extends EventMap>(): TypedEmitter<T2> & TypedEmitter<{ spawn: (name: string) => void }> })<T1> {
constructor() {
super();
this.emit("spawn", "abc");
}
}
class Frog extends Animal<{ jump: (times: number) => void }> {
constructor() {
super();
this.emit("spawn", "bcd");
this.emit("jump", 3);
}
}
class Bird<E extends EventMap = { test: () => void }> extends Animal<{ fly: () => void } & E> {}
const frog = new Frog();
const bird = new Bird();
const animals: Animal[] = [frog, bird];
animals[0].addListener("spawn", (name) => {});
frog.addListener("jump", (times) => {});
bird.addListener("test", () => {});