【问题标题】:Why cant typescript infer the return type when using Differentiating Type checks为什么在使用区分类型检查时打字稿不能推断返回类型
【发布时间】:2020-12-17 14:35:34
【问题描述】:

我想了解为什么 TypeScript 不能推断以下函数的返回类型(虽然它能够在 if-else 语句中进行区分):

function calc (arg: number|string) {
  if (typeof arg === 'number') {
    // here typescript knows arg is number type
    return arg 
  } else if (typeof arg === 'string') {
    // here typescript knows arg is string type
    return arg 
  }
}

// infers test to be  number|string|undefined
const test = calc(10)

而当我们编写如下函数时:

function calc (arg: number|string) {
  return String(arg)
}

// infers test to be string
const test = calc(10)

那么它当然可以推断出返回类型。但是为什么它不能推断第一个函数的返回类型,而它确实在不同的分支中提供了类型安全,并且它知道它得到一个数字类型作为 arg?

编辑

我知道怎么解决,但是我很想知道为什么TypeScript不能解决?

【问题讨论】:

  • 使用重载签名
  • 因为calc 的第一个实现的推断类型是(arg: number | string) => number | string | undefined,它不会神奇地推断出重载或泛型。
  • 您在示例中返回的route 是什么?
  • @paroxyzm 错误 -> 忘记更改为 arg

标签: typescript


【解决方案1】:
function calc (arg: number): number;
function calc (arg: string): string;
function calc (arg: number|string) {
  if (typeof arg === 'number') {
    // here typescript knows arg is number type
    return route
  } else if (typeof arg === 'string') {
    // here typescript knows arg is string type
    return route
  }
}

您可以通过重载原始函数定义来指定特定参数类型的返回值。

另请参阅:https://www.typescriptlang.org/docs/handbook/functions.html#overloads

【讨论】:

    【解决方案2】:

    如果返回类型应该与输入相同,您也可以使用 Generic

    function calc <T extends number|string>(arg: T): T {
      if (typeof arg === 'number') {
        // here typescript knows arg is number type
        return arg
      } else if (typeof arg === 'string') {
        // here typescript knows arg is string type
        return arg
      }
      throw new Error('should not reach here')
    }
    
    const t = calc(12)
    

    【讨论】:

    • 值得注意的是,使用这种方法,您的输入和输出类型必须是对称的。
    【解决方案3】:

    Typescript 不会自动执行此操作,因为这需要评估您的函数,即实际运行代码。即使参数在编译时是已知的,理论上它可能不会尝试这样的事情。也许在这种情况下,它看起来应该很简单,但在一般情况下并非如此。

    当它在块内为您缩小类型时,它只是说如果条件为真,则类型为 X,但它实际上并没有用值评估条件。

    【讨论】:

    • 不行,编译器可以看到如果number返回一个数字,如果string返回一个字符串,否则不返回。所以它确实评估代码。
    • @crashmstr TS 可以看到所有代码路径的集合并告诉您每个返回的类型,但它不会缩小到特定的代码路径并根据值推断返回类型论据。
    【解决方案4】:

    您拥有的函数本质上只是一个身份函数,它将返回参数。 因此,如果参数是 X 类型,则返回类型也必须是 X 类型(至少)。

    您没有指定所有分支(else 块)所以理论上它也可以返回undefined

    因此它推断整体类型应该是: X | undefined -> string | number | undefined

    它还能推断出什么?

    在第一个示例中,您强制函数返回 String(arg),因此无论参数是什么,它总是返回一个字符串。

    【讨论】:

    • 是的,但我用一个整数和预期的打字稿调用函数来通过函数评估它,因为这些块现在只有一个结果是可能的。但是,据我所知,打字稿实际上并没有评估这些块。
    【解决方案5】:

    在代码中

    function calc (arg: number|string) {
      if (typeof arg === 'number') {
        // here typescript knows arg is number type
        return arg 
      } else if (typeof arg === 'string') {
        // here typescript knows arg is string type
        return arg 
      }
    }
    

    代码共有三种路径:

    1. 号码
    2. 字符串
    3. 数字或字符串以外的东西

    第三种情况,没有返回类型,所以返回的签名是number | string | undefined

    如果您检查TypeScript playground 中的代码,您可以看到这一点

    function calc(arg: number | string): string | number | undefined
    并非所有代码路径都返回值。(7030)

    如果您希望您的变量根据传入的参数“成为”单一类型,您可以执行以下操作:

    function calc3<T>(arg: T) {
        return arg 
    }
    
    let isAnInt = calc3(3);
    let isAString = calc3('hello');
    

    其中isAnInt 输入为数字,isAString 输入为字符串,这取决于传递给函数的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 2019-04-10
      • 1970-01-01
      • 2020-12-20
      相关资源
      最近更新 更多