类型 - TypeScript 方法重载
我有这样的代码,一个有两个重载的方法:
/**
* 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]) {
// ...
}
}
它应该这样使用:
type MyHooks = {
aaa: void;
bbb: { data: string }
};
let h = new Hooks<MyHooks>();
h.fireHooks('aaa');
h.fireHooks('bbb', { data: 'data' });
有一个通用类Hooks,里面有一个方法fireHooks,它接受事件名称到上下文数据的映射,用于验证事件的上下文。有些事件可以在没有任何上下文的情况下调用,所以我希望这个方法只用一个参数调用这些事件。
一切都运作正常,但问题是这些重载看起来很丑且太过头。
如果我跳过重载,TS 在 h.fireHooks('aaa'); 上会报错
Expected 2 arguments, but got 1.ts(2554)
如果我只跳过第二个重载,这个重载似乎是实现的 100% 拷贝,TS 在 h.fireHooks('bbb', { data: 'data' }); 上会报错
Argument of type '"bbb"' is not assignable to parameter of type '"aaa"'.ts(2345)
有人能解释一下吗
- 为什么我需要第一个重载才能跳过参数
- 以及为什么我需要第二个重载,它似乎是多余的,因为它与实现完全相同?
答案#1
“为什么我需要第二个重载,它看起来是多余的,因为它和实现完全一样?”
当你使用重载时,你实际上无法直接调用实现。
观察:
function foo(a: string, b: string): void;
function foo(a: number, b: number): void;
function foo(a: number | string, b: number | string): void {
//...
}
foo(1, 2) // good
foo('a', 'b') // good
foo(1, 'b') // Error: No overload matches this call.
即使 foo 的实现允许一个参数是数字,另一个是字符串,重载也不允许这样做。
所以你永远不会只有一个重载和一个实现。那两者将是相同的。
你要么有一个带有一个签名的函数,要么有两个或更多重载以及一个实现。
“为什么我需要第一个重载才能跳过参数”
// You can also leave off the argument entirely
fireHooks<K extends KeysOfType<EventsMap, void>>(
event: K,
): Promise<void>;
因为你有两个函数签名,并且你需要为每个签名提供一个重载,因为(如上所述)你不能直接调用实现。每个签名的event类型不同,因为它们操作不同的键。
