I've encountered an issue with a code snippet that has a 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]) {
// ...
}
}
This code should be utilized in the following way:
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 a method fireHooks
that accepts an event name to context data map to verify the event's context. Some events can be called without any context, hence the need for the method to be able to accept one argument for those events.
Although the functionality works as intended, the issue lies in the fact that these overloads appear cumbersome and unnecessary.
If I omit the overloads, TypeScript raises an error for h.fireHooks('aaa');
stating:
Expected 2 arguments, but got 1.ts(2554)
On the other hand, if I exclude only the second overload, which mirrors the implementation, TypeScript throws an error for
h.fireHooks('bbb', { data: 'data' });
with:
Argument of type '"bbb"' is not assignable to parameter of type '"aaa"'.ts(2345)
Can someone clarify:
- why the first overload is necessary to allow skipping a parameter
- and why the second overload, which appears redundant as it duplicates the implementation, is required?