How to infer type of overloaded method in generic function?

  Kiến thức lập trình

I am trying to create a generic function that would subscribe to an event emitter. The subscribe function would accept 3 arguments: event name, event handler and the event emitter to attach to.

How to make it correctly infer type definitions, especially for the event handler, so when I pass, for example, process object as an event emitter and 'warning' as an event name, I would not have to explicitly declare handler arguments’ types:

subscribe('warning', (warning) => {/* */}, process);
//                    ^
//                    the `warning` argument should have `Error` type as defined 
//                    in `process` interface:
//
//                    interface Process {
//                      ...
//                      addListener(event: "warning", listener: WarningListener): this;
//                    }
//                    ...
//
//                    type WarningListener = (warning: Error) => void;

Here is my implementation along with the example usage. However, it only works with the second event (event 'b', and not with event 'a').

interface Target<N, H> {
  on(name: N, handler: H): unknown;
}

const subscribe = <
  N extends T extends Target<infer N, unknown> ? N : never,
  H extends T extends Target<N, infer H> ? H : never,
  T extends Target<unknown, unknown>,
>(
  name: N,
  handler: H,
  target: T
) => {
  target.on(name, handler);
};

const target: {
  on(name: 'a', handler: (a1: string) => void): void;
  on(name: 'b', handler: (b1: number) => void): void;
} = {
  on: ()=>{},
};

// errors with: Argument of type '"a"' is not assignable to parameter of type '"b"'.
subscribe('a', (a1) => console.log(a1), target);


// works fine
subscribe('b', (b1) => console.log(b1), target); 

New contributor

Maksim Kalinin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

LEAVE A COMMENT