我正在尝试创建一个函数,将其参数传递给另一个函数。这两个函数需要具有相同的重载。
function original (a: number): boolean;
function original (a: string, b: string): boolean;
function original (a: number | string, b?: string): boolean {
return true;
}
function pass (a: number): boolean;
function pass (a: string, b: string): boolean;
function pass (a: number | string, b?: string): boolean {
return original(a, b);
}
这不起作用。
'string | 类型的参数“数字”不可分配给“字符串”类型的参数。
“数字”类型不可分配给“字符串”类型。(2345) input.tsx(4, 10):针对此实现的调用将成功,但重载的实现签名在外部不可见。
您可以使用
typeof运算符将
pass
函数声明为 original
函数的类型,例如
function original (a: number): boolean;
function original (a: string, b: string): boolean;
function original (): boolean {
return true;
}
let pass: typeof original = () => {
return true;
};
pass(5); // all good
pass('a', 'b'); // all good
pass(45, 'b'); // error here
添加额外的过载:
function original<
A extends number | string,
B extends A extends string ? string : undefined
>(a: A, b: B): boolean;
现在看起来像:
function original(a: number): boolean;
function original(a: string, b: string): boolean;
function original<
A extends number | string,
B extends A extends string ? string : undefined
>(a: A, b: B): boolean;
function original(a: number | string, b?: string): boolean {
return true;
}
function pass(a: number): boolean;
function pass(a: string, b: string): boolean;
function pass(a: number | string, b?: string): boolean {
return original(a, b); // Works
}
const t1 = original(1); // Works
const t2 = original("foo", "foo"); // Works
const t3 = original(1, "foo"); // Works: gives an error as expected
const t4 = original("foo"); // Works: gives an error as expected
您不再需要第二个重载
function original(a: string, b: string): boolean
,但您仍然可以保留它以提高代码的可读性。
大多数情况下,函数重载的实现接收最宽松的类型,“任何”或“未知”。但“any”会让你失去类型(所有相关变量都会变成“any”的类型),所以“未知”是唯一的选择。 为什么实现接收最宽松的类型?
您重载该函数只是因为您希望该函数接受不同类型/数量的参数。但是,如何知道调用函数时传入的参数类型呢?你不知道,你必须先做一些类型检查。
function original (a: number): boolean;
function original (a: string, b: string): boolean;
function original (a: unknown, b?: unknown): boolean {
return true;
}
function pass (a: number): boolean;
function pass (a: string, b: string): boolean;
function pass (a: unknown, b?: unknown): boolean {
if(typeof a === "number" && typeof b === "undefined"){
return original(a);
}
if(typeof a === "string" && typeof b ==="string"){
return original(a, b)
}
return false; // maybe, depends on your logic
}
由于实现函数的类型签名在外部不可见,因此您不会获得
pass(unknown, unknown)
签名,只会建议实现上面的两个签名。
老问题,但有不同的选择。只需直接引用
original
的签名和返回类型即可,而不用重新实现它们。
function original (a: number): boolean;
function original (a: string, b: string): boolean;
function original (): boolean {
return true;
}
function pass(
...args: Parameters<typeof original>
): ReturnType<typeof original> {
return original(...args);
}
Intellisense 足够了解超载签名,因此当您输入
pass(
时,您将获得相关信息。另外你只需要维持原来的功能即可。
这是一个不使用类型断言并且不会更改或添加任何重载签名到任一函数的解决方案:使用 IIFE 创建这两个函数,其中函数在 IIFE 的“私有”范围内具有较弱的签名,但外面的签名更强。
interface OriginalSignature {
(a: number): boolean;
(a: string, b: string): boolean;
}
const [original, pass] = (function(): [OriginalSignature, OriginalSignature] {
function original(a: number | string, b?: string): boolean {
return true;
}
function pass(a: number | string, b?: string): boolean {
return original(a, b);
}
return [original, pass];
})();
测试:
// OK
original(1);
original('a', 'b');
pass(1);
pass('a', 'b');
// errors
original('a');
original(1, 'b');
pass('a');
pass(1, 'b');
在实现中强制转换为任意 ;)。使用重载签名,您可以确定除了允许的参数之外没有其他内容。
function original (a: number): boolean;
function original (a: string, b: string): boolean;
function original (a: number | string, b?: string): boolean {
return true;
}
function pass (a: number): boolean;
function pass (a: string, b: string): boolean;
function pass (a: number | string, b?: string): boolean {
return original(a as any, b as any); // here, you are sure only
// expected arguments may appear - so the cast operation is safe.
}
或者使用命名元组代替重载(效果与重载类似)。
function original(...args: [a: string, b: string] | [a: number]): boolean {
return true;
}
function pass (...args: [a: string, b: string] | [a: number]): boolean {
return original(...args);
}