forEach 循环中设置的变量类型错误

问题描述

forEach 回调函数内部设置变量时,该变量的类型似乎不正确。一个简单的例子:

let foo: (string | null) = null;

[1,2,3].forEach((i) => {
  foo = "bar";
});

if(foo == null) {
    throw new Error("not found")
}

# Typescript complains that `length` is not a property of type `never`
console.log(foo.length)

Typescript Playground link

就在 forEach 之后,我希望 foo 的类型为 string | null,但实际上它只是 null。在 console.log 行中,我希望它只是 string,但实际上它是 never(因为我们已经消除了 null 作为 {{1} } 投掷。

重要的是,上面的代码(减去 Error 位)在纯 JavaScript 中工作得非常好 - 这纯粹是我和 TypeScript 编译器以某种方式无法正常通信的情况。

无论如何:

  1. 如果有人能解释为什么这样我就可以更好地理解 Typescript 了,我会很高兴
  2. 我该如何解决这个问题?我可以在 console.log 之前打一个 : (string | null),但肯定有更好的方法吗?

解决方法

这是控制流分析的权衡之一。

通过注释 string | null 类型,您告诉编译器该变量显然需要任一类型的值。然后您对 null 进行赋值,这让编译器将类型缩小到 null

您希望 TypeScript 考虑回调内部的分配并放弃缩小范围,但事实并非如此。这是团队在决定作为参数传递给高阶函数的函数中发生的突变是否应该泄漏到外部作用域时所做的权衡。

请参阅关于此问题的 this source repo 讨论。

,

这不是 TypeScript 的行为,而是 EcmaScript 的行为。 forEach 定义了变量的作用域,所以里面发生的事情,它仍然留在里面。

看看这里,例如:https://alligator.io/js/foreach-vs-for-loops/

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...