我目前正在研究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的智能感知,result
变量的类型为boolean | undefined
。
即使我已经测试过bar
参数,undefined
怎么可能foo
?如果foo
存在,是否应该通过类型推断来预测bar
也存在?
答案 0 :(得分:3)
这里的第一个问题是,允许重载函数的实现签名比任何调用签名都宽松。在实现内部,编译器仅对照实现签名进行检查。这意味着在您的函数中,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
}
}
好的,希望能有所帮助;祝你好运!