打字稿重载、可选参数和类型推断

Posted

技术标签:

【中文标题】打字稿重载、可选参数和类型推断【英文标题】:Typescript overloads, optional arguments and type inference 【发布时间】:2019-09-15 01:58:49 【问题描述】:

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

即使我已经测试了foo 参数,为什么bar 可以是undefined?如果fooexists,类型推断不应该预测barexists 也存在吗?

【问题讨论】:

你可以看看这个答案:Is there a way to do method overloading in TypeScript? 【参考方案1】:

这里的第一个问题是允许重载函数的实现签名比任何调用签名都宽松。在实现内部,编译器只检查实现签名。这意味着在您的函数内部,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 ?
    

【讨论】:

以上是关于打字稿重载、可选参数和类型推断的主要内容,如果未能解决你的问题,请参考以下文章

打字稿:在没有实际扩展/扩大的情况下捕获/推断类型的约束

打字稿没有正确推断剩余参数

打字稿不推断通用对象的值

文字类型推断 - 打字稿

基于可选参数存在而不使用函数重载的打字稿函数返回类型

打字稿:在对象内推断通用对象的类型