Related to the accepted answer: https://stackoverflow.com/a/63639280/17928771
EventEmitter3 is a generic class that takes an (as one example) Interface of Events/Handlers. I'm trying to restrict IRaceManager
emit
s to IRaceEvents
. I have tried:
class POSRaceManager implements IRaceManager extends EventEmitter<IRaceEvents> {}
let raceManager: IRaceManager = new POSRaceManager();
raceManager.emit('moo'); // error, no `emit` on IRaceManager
interface IRaceManager extends EventEmitter<IRaceEvents> {} // TS2749: 'EventEmitter' refers to a value, but is being used as a type here. Did you mean 'typeof EventEmitter'?
The following attempt works, but doesn't limit the emit
or on
to the IRaceEvents (or an extension of IRaceEvents)
This fails:
type RaceEventEmitterType<T extends IRaceEvents> = InstanceType<typeof EventEmitter>;
type IRaceManager<T extends IRaceEvents> = RaceEventEmitterType<T>;
let raceManager: IRaceManager = new POSRaceManager<IRaceEvents>;
raceManager.emit("moo"); // no error because of `InstanceType<typeof EventEmitter>`
type RaceEventEmitterType<T extends IRaceEvents> = InstanceType<typeof EventEmitter<IRaceEvents>>; // Need to investigate what this does, actually. I overlooked a syntax error before.
Any ideas to restrict emissions from raceManager<?>
to T extends IRaceEvents
only?
I am currently settled on the following (which works, I am just wondering if there is a way to resolve as above):
type RaceEventEmitter<T extends IRaceEvents> = InstanceType<typeof EventEmitter>;
type IRaceManager<T extends IRaceEvents> = RaceEventEmitter<T>;
class POSRaceManager extends EventEmitter<IRaceEvents> implements IRaceManager<IRaceEvents>;
"What part don't you understand?"
Answer: Core concept? (Archer quote)
This has been a rough learning curve but finally have a solution to move forward:
type TRaceEvents = {
raceUpdate: (race: IRace | null) => void;
}
//=> Here, Extending EventEmitter is essential to ensure the implementation properly
// extends `EventEmitter`; however, `extends EventEmitter<TRaceEvents>` has
// no additional affect (though is preferred as the Runner could fail to
// function properly if the impl isn't Typed with `TRaceEvents`)
interface IRaceManager extends EventEmitter {
race: IRace;
start() => void;
}
class StandardRaceManager extends EventEmitter<TRaceEvents> implements IRaceManager {
constructor(public race: IRace) { super(); }
start() {
this.race.status = "Running";
//=> Here, `this.race` red squiggled. Why?
// this.emit("raceUpdate", this.race);
//=> Here, no red squiggly, but casting to `any` is no bueno?
this.emit("raceUpdate", this.race as any);
}
const standardRace: IRaceManager = new StandardRaceManager({_id: 1, status: "taxiing"} as IRace);
//=> Here, race is properly typed with `IRace | null`
standardRace.on("raceUpdate", race => console.log(race));
standardRace.start();
//=> No red squiggly. Why?
standardRace.emit("raceUpdate", race);
Perhaps I've been fighting (and continue to fight with) with an IDE error this whole time.