Typescript重载,可选参数和类型推断

时间:2019-04-25 15:15:31

标签: typescript overloading type-inference

我目前正在研究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也存在?

1 个答案:

答案 0 :(得分:3)

这里的第一个问题是,允许重载函数的实现签名比任何调用签名都宽松。在实现内部,编译器仅对照实现签名进行检查。这意味着在您的函数中,foobar都与类型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知道argsmethod()是空元组或一对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但忘记了barfoo有任何关系。解决此问题的一种方法不是将foobar分解为单独的类型,而是对单个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 
    }
}

好的,希望能有所帮助;祝你好运!