I have a code like this, method with 2 overloads:
/**
* Returns all keys of object that have specific value:
* @example
* KeysOfType<{a:1, b:2, c:1}, 1> == 'a' | 'c'
*/
type KeysOfType<MapT extends Record<string, any>, ValT> = {
[K in keyof MapT]: MapT[K] extends ValT ? K : never;
}[keyof MapT];
export class Hooks<EventsMap extends Record<string, any>> {
fireHooks<K extends KeysOfType<EventsMap, void>>(
event: K,
context: void,
): Promise<void>;
fireHooks<K extends keyof EventsMap>(
event: K,
context: EventsMap[K],
): Promise<void>;
fireHooks<K extends keyof EventsMap>(event: K, context: EventsMap[K]) {
// ...
}
}
It is supposed to be used like that:
type MyHooks = {
aaa: void;
bbb: { data: string }
};
let h = new Hooks<MyHooks>();
h.fireHooks('aaa');
h.fireHooks('bbb', { data: 'data' });
There is a generic class Hooks with method fireHooks that takes events name-to-context-data map to validate context of event.
Some of the events can be called w/o any context, so I want this method to call these events with only one argument.
Everything works fine, but the problem is that these overloads look ugly and overkill.
If I skip overloads, TS complains on h.fireHooks('aaa'); with
Expected 2 arguments, but got 1.ts(2554)
If I skip only 2nd overload, which seem to be 100% copy of implementation, TS complains on h.fireHooks('bbb', { data: 'data' }); with
Argument of type '"bbb"' is not assignable to parameter of type '"aaa"'.ts(2345)
Can someone explain
- why do I need 1st overload to be able to skip parameter
- and why I need 2nd overload, which seem to be redundant as it completely the same as implementation?