Closed jgornick closed 4 years ago
Hey @jgornick, thanks for reporting!
I have had a look at it, but unfortunately I wasn't able to find a solution yet.
If someone else has an idea... The sample code looks something like this:
import EventEmitter from "events"
import TypedEventEmitter from "typed-emitter"
interface FooEvents {
bar(text: string): void
}
export default class FooEmitter extends EventEmitter implements TypedEventEmitter<FooEvents> {}
To get around the issue for now, I created a delegation class that's typed properly:
import { EventEmitter as CoreEventEmitter } from 'events'
import TypedEmitter from 'typed-emitter'
type Arguments<T> = [T] extends [(...args: infer U) => any]
? U
: [T] extends [void] ? [] : [T]
export class EventEmitter<Events> implements TypedEmitter<Events> {
public eventEmitter = new CoreEventEmitter() as unknown as TypedEmitter<Events>
public addListener<E extends keyof Events> (event: E, listener: Events[E]): this {
this.eventEmitter.addListener(event, listener)
return this
}
public on<E extends keyof Events> (event: E, listener: Events[E]): this {
this.eventEmitter.on(event, listener)
return this
}
public once<E extends keyof Events> (event: E, listener: Events[E]): this {
this.eventEmitter.once(event, listener)
return this
}
public prependListener<E extends keyof Events> (event: E, listener: Events[E]): this {
this.eventEmitter.prependListener(event, listener)
return this
}
public prependOnceListener<E extends keyof Events> (event: E, listener: Events[E]): this {
this.eventEmitter.prependOnceListener(event, listener)
return this
}
public off<E extends keyof Events>(event: E, listener: Events[E]): this {
this.eventEmitter.off(event, listener)
return this
}
public removeAllListeners<E extends keyof Events> (event: E): this {
this.eventEmitter.removeAllListeners(event)
return this
}
public removeListener<E extends keyof Events> (event: E, listener: Events[E]): this {
this.eventEmitter.removeListener(event, listener)
return this
}
public emit<E extends keyof Events> (event: E, ...args: Arguments<Events[E]>): boolean {
return this.eventEmitter.emit(event, ...args)
}
public eventNames (): (keyof Events)[] {
return this.eventEmitter.eventNames() as (keyof Events)[]
}
public listeners<E extends keyof Events> (event: E): Function[] {
return this.eventEmitter.listeners(event)
}
public listenerCount<E extends keyof Events> (event: E): number {
return this.eventEmitter.listenerCount(event)
}
public getMaxListeners (): number {
return this.eventEmitter.getMaxListeners()
}
public setMaxListeners (maxListeners: number): this {
this.eventEmitter.setMaxListeners(maxListeners)
return this
}
}
The strict-event-emitter-types package solves the extending EventEmitter problem with this:
class MyEventEmitter extends (EventEmitter as { new(): MyEmitter }) {
}
Would it help here?
That looks like a pretty nifty solution. Thanks for sharing, @yoursunny!
Will check it out.
So I tried that trick, but it doesn't work for this use case that way.
I propose to solve this issue for now by changing the eventNames()
signature:
- eventNames (): (keyof Events)[]
+ eventNames (): (keyof Events | string | symbol)[]
Pro:
Con:
.eventNames()
to random strings and TS will not complain if you compare it to a string that's not an event of the emitterFix published as v1.1.0 🚀
For example:
Results in error:
Workaround is to implement
eventNames
and override typing: