Skip to content

TypeScript: Cannot infer event argument types from instance of class which is extended from EventEmitter #276

Description

@vladkrutenyuk

Hi, I’m trying to create a generic React hook that listens to an EventEmitter3 instance and updates state based on a selected transformation of the event arguments.

Here’s a simplified example:

function useEventEmitterState<
  TState,
  TEmitter extends EventEmitter,
  TEvent extends ReturnType<TEmitter["eventNames"]>[number]
>(
  emitter: TEmitter,
  event: TEvent,
  select: (...args: any[]) => TState,
  init: (() => TState) | TState
) {
  const [state, setState] = useState<TState>(init);

  useEffect(() => {
    if (!emitter) return;

    const listener = (...args: any[]) => {
      setState(select(...args));
    };

    emitter.on(event, listener);
    return () => emitter.off(event, listener);
  }, [emitter, event]);

  return state;
}

Problem:

TypeScript cannot infer the argument types for select based on the event name TEvent and the emitter instance TEmitter.

Currently I have to use any[] for ...args in select and the internal listener.

I would like a way to type select so that:

const state = useEventEmitterState(emitter, "smthChanged", (arg1, arg2) => { ... }, ...);

TypeScript automatically infers arguments for event name.

Request:

Could the EventEmitter3 typings support inferring event argument types from the generic EventMap of the emitter instance? Something similar to:

interface MyEvents {
  fileSelected: [File];
  otherEvent: [string, number];
}
class Example extends EventEmitter<MyEvents> {
  selectedFile: File | null = null;
}
const emitter = new Example();

function MyComponent() { 
  // select callback should infer args based on "fileSelected"

  const selectedFile = useEventEmitterState<File | null>(emitter, "fileSelected", (file) => file, emitter.selectedFile);
  const someOtherState = useEventEmitterState<string>(emitter, "otherEvent", (arg1,arg2) => `${arg1}${arg2}`, "");

  return <div>hi</div>
}

Right now, using only ReturnType<TEmitter["eventNames"]>[number] is insufficient, because TS loses the argument types.

Thanks!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions