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