【问题标题】:TypeScript Type Narrowing Error with forEachforEach 的 TypeScript 类型缩小错误
【发布时间】:2020-08-03 14:46:20
【问题描述】:

请帮助我了解 TS 如何缩小类型。我have 一个简单的foo 函数,它在任意数组上具有forEach 迭代器。很明显console.log在执行这段代码后会记录false,但是TS坚持是true,这是错误的。

我的期望是如果TS因为可能的异步而无法处理forEach或类似的功能,那么它应该建议boolean。这种情况真的让我很沮丧,也降低了我的工作效率,因为我需要重新检查所有内容 10 次,以证明 TS 错误并且我的代码是正确的。

function foo() {
  let canActivate = true;

  ['foo'].forEach(() => {
    canActivate = false;
  })

  console.log(canActivate);
}

foo();

【问题讨论】:

  • canActivateforEach 回调中的类型是什么?
  • 另外,您使用的是什么版本的 TypeScript?您是否在他们的 GitHub 存储库中将此作为问题提交?
  • 尝试使用var而不是let
  • @Dai canActivate type in forEach 和外面一样,因为是同一个变量。 TS 版本是 3.9.2。不,我没有填写问题,因为我认为这不是错误并且可以以某种方式解释。
  • @AlvinStefanus 我认为我们不应该在现代代码中使用var。无论如何,var 的建议是一样的:(local var) canActivate: true

标签: typescript


【解决方案1】:

This excellent blog post 解释说这是一项功能,而不是错误。我将在这里引用一大段内容(强调):

示例

let a: number | null = 42

makeSideEffect()

a // is `a` still a number?

function makeSideEffect() {   
  // omitted... 
} 

...

人们可能会要求 [the] 编译器推断 makeSideEffect 做了什么,因为我们可以 提供函数的来源。然而这实际上并不 可行,因为环境功能和(可能是多态的) 递归。 如果我们指示编译器将陷入无限循环 它推断任意深度函数,作为停止问题本身。

所以一个现实的编译器必须通过一个函数来猜测一个函数做了什么 一致的策略。自然我们有两种选择:

  • 假设每个函数都没有相关的副作用:例如 分配像a = null。我们称之为乐观。
  • 假设每个 功能确实有副作用。我们称这种策略为悲观策略。

剧透:TypeScript 使用乐观策略。


另一个简短的摘录:

没有关键字会告诉编译器是否会调用回调函数 立即,静态分析也不会告诉函数的行为: setTimeoutforEach在编译器看来是一样的。

所以下面的例子不会编译。

var a: string | number = 42 // smart cast to number
someArray.forEach(() => {
  a.toFixed() // error, string | number does not have method `toFixed`
})

所以你有它。该帖子还解释说,没有解决方案可以让 TypeScript 识别 forEach 函数或除立即调用函数之外的任何函数的副作用。这意味着您可以:

  • 使用regular for loop 而不是forEach,这会导致最终的canActivate 被正确推断为boolean 而不是true
  • 尽可能使用立即调用的函数。或者...
  • 在使用 FP-style features(如 mapfilterforEach 等)时坚持函数式编程 (FP) 范例。这意味着目标是不变性、无副作用等。

总而言之,这不是什么好消息。希望将来 TypeScript 能够获得某种功能,使我们能够在函数修改某个变量时向编译器发出警告——至少可以防止过早地假设某些开始为 true 的东西将永远保持不变。

【讨论】:

  • 哇,很棒的反应,非常感谢!让我大开眼界的是,TS 不会查看回调函数内部,即使它们与执行流程同步执行。
【解决方案2】:

您似乎在中间某处被console.log 副作用打断了类型推断)Please look here at line 11

此外,TS 不会从像 forEach 这样的副作用推断类型。这是设计使然。有一堆 TS 特性可以帮助 TS 推断类型。看看这篇文章 https://www.typescriptlang.org/docs/handbook/advanced-types.html

【讨论】:

  • 如果您在示例中将鼠标悬停在第 8 行的canActivate 上,您将得到let canActivate: true,这是错误的,所以不是因为console.log
  • 恕我直言,在这种情况下,最好明确定义类型。 let canActivate: boolean = true;你怎么看?
  • 但是明确的boolean 在这种情况下也不起作用。 TS 将boolean 缩小到true
猜你喜欢
  • 2020-07-22
  • 2020-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-12
  • 1970-01-01
  • 2021-02-26
  • 2021-12-17
相关资源
最近更新 更多