我目前正在寻找Typescript中的重载。
假设我有一个带有一个重载的函数:
function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(foo?: boolean, bar?: boolean) {
if (foo === true || foo === false) {
const result = bar;
}
}
函数是在没有参数的情况下调用的,或者是用两个参数调用的(foo和bar)。根据vscode的intellisense,result
变量有一种boolean | undefined
。
即使我测试了bar
参数,为什么undefined
可以成为foo
?如果foo
exists,不应该类型推断预测bar
exists也?
这里的第一个问题是允许重载函数的实现签名比任何调用签名更松散。在实现中,编译器仅检查实现签名。这意味着在你的函数内部,foo
和bar
都独立于boolean | undefined
类型,并且没有办法恢复调用该方法的任何人都指定两者或两者都没有的事实。
TypeScript最近添加了对rest/spread tuples in function parameters的支持,因此您可以像这样重写您的函数签名:
declare function method(...args: [] | [boolean, boolean]);
method(); // okay
method(false); // errror
method(true, false); // okay
现在TypeScript知道args
到method()
要么是空元组,要么是一对boolean
值。如果需要,可以保留重载,只需使实现签名更窄:
function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(...args: [] | [boolean, boolean]) {
const foo = args[0];
const bar = args[1];
if (foo === true || foo === false) {
const result = bar; // oops, still boolean | undefined
}
}
不幸的是,推理仍然不起作用,这是第二个问题:TypeScript的控制流分析并不像我们那样聪明。虽然我们知道foo
的类型与bar
的类型相关,但编译器却没有。如果缩小foo
但忘记了bar
与foo
有任何关系。修复此问题的一种方法是不将foo
和bar
拆分为单独的类型,而是在单个args
变量上使用属性访问类型保护。当args
从[] | [boolean, boolean]
缩小到[boolean, boolean]
时,你可以确定定义了第二个元素:
function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(...args: [] | [boolean, boolean]) {
if ('0' in args) {
const result = args[1]; // boolean
}
}
这可能都是代码更改太多,而IntelliSense对您来说并不值得。如果是这样,并且你觉得编译器更聪明,你可以使用type assertion继续你的一天:
function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(foo?: boolean, bar?: boolean) {
if (foo === true || foo === false) {
const result = bar as boolean; // I'm smarter than the compiler 🤓
}
}
好的,希望有所帮助;祝好运!