1

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 emits to IRaceEvents. I have tried:

Attempt 1.

class POSRaceManager implements IRaceManager extends EventEmitter<IRaceEvents> {}

let raceManager: IRaceManager = new POSRaceManager();
raceManager.emit('moo'); // error, no `emit` on IRaceManager

Attempt 2.

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)

Attempt 3.

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>`

Final attempt.

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?

Edit:

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's wrong with attempt 1? – Ben Jones Dec 30 '22 at 21:18
  • `// error, no 'emit' on IRaceManager`. I found and edited a mistake in the final attempt; however, since I `settled` and shifted away, I no longer have the code in a state to test. Nor do I have the time. Should this post be removed or do you feel it is beneficial? – Kevin Biesbrock Dec 30 '22 at 22:52

1 Answers1

0

"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.