I am currently working on developing a versatile function that can subscribe to an event emitter. The function subscribe
is designed to take 3 arguments: event name, event handler, and the event emitter to connect to.
I am looking for ways to ensure accurate type definitions inference, especially for the event handler. This way, when I provide, for example, the process
object as an event emitter and 'warning'
as the event name, I won't need to explicitly specify the types of handler arguments:
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;
Below is my implementation along with an example usage. However, it only seems to function properly with the second 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);