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

Variable set in forEach loop getting wrong type

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 作为 [=20= 的可能性) ] 扔.

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

总之:

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

这不是 TypeScript 的行为,而是 EcmaScript 的行为。 forEach 为变量定义了一个范围,所以里面发生了什么,它仍然在里面。

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

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

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

您希望 TypeScript 考虑回调内的赋值并放弃缩小,但它没有。这是团队在决定作为参数传递给高阶函数的函数中发生的突变是否应该泄漏到外部作用域时做出的权衡。

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