3

如果条件逻辑被包装到单独的函数中,这是 TypeScript 中缺少该功能还是经过深思熟虑的 TS 编译器无法推断(然后缩小)参数的类型?我想到的是在这种情况下 TS 编译器给我的错误:

function isString(s) {
  return typeof s === 'string';
}

function toUpperCase(x: unknown) {
  if(isString(x)) {
    x.toUpperCase(); // ⚡️ x is still of type unknown
  }
}

但是,如果我摆脱该函数并在语句isString中内联其逻辑,那么 TS 编译器就会很好地了解该类型。if

我知道我可以使用as string或使用类型谓词来转换类型,并在函数中使用is string类型注释。isString但我刚刚开始思考——为什么TS不能缩小类型了x

4

1 回答 1

3

一种可行的方法是编译器在执行控制流分析时内联一些函数调用,这意味着:

function toUpperCase(x: unknown) {
  if (isString(x)) {
     x.toUpperCase(); 
  }
}

需要isString()像这样扩展的主体进行分析:

function toUpperCase(x: unknown) {
  if (typeof x === "string") {
    x.toUpperCase();
  }
}

GitHub 中讨论此问题的规范问题是microsoft/TypeScript#9998。那个问题提出的问题是“当一个函数被调用时,我们应该假设它的副作用是什么?” 而且似乎没有完美的答案。据说,所有函数调用的完整内联解决方案“甚至都不实用”。 是否可以isString()在不完全拖累编译器的情况下按照您的意愿进行浅层内联?可能,但值得吗?没有把握。看起来那里没有太大进展。如果您对此感觉足够强烈并且有一些令人信服的想法,您可以为 GitHub 问题做出贡献。


另一种工作方式是,如果编译器自动推断类型谓词返回类型为正确类型的boolean-returning 函数,这意味着:

function isString(s: unknown) {
  return typeof s === "string";
}

需要像这样推断:

function isString(s: unknown): s is string {
  return typeof s === "string";
}

GitHub 中讨论此问题的规范问题是microsoft/TypeScript#16069。以前的版本因为过于复杂而被拒绝,因为编译器无法分析每个布尔返回函数并确定其返回类型是否有任何类型谓词的含义。不过,#16069 仍然是开放的,大概是为了摘下“简单”函数(如x => typeof x === "string". 同样,目前尚不清楚那里是否取得了任何进展,因此,如果您对此有足够的信心并有一些令人信服的想法,您可以为 GitHub 问题做出贡献。


但在这两种情况下,如果我是你,我都不会屏住呼吸。TS3.6 的惯用解决方案是将注释isString()作为用户定义的类型保护返回s is string并继续处理其他事情。

好的,希望对你有帮助。祝你好运!

于 2019-09-27T01:03:28.387 回答