一般的答案是,在这种情况下你比编译器更聪明。 TypeScript 使用启发式方法分析控制流程尝试推断表达式的更窄类型的代码。它在这方面做得很合理,但是它并不完美也许永远不可能。
当您访问obj.bar
抛出后立即 ifisA(obj)
回报false
,编译器缩小范围obj
包括A
,如您所料。不幸的是,当您创建一个闭包并将其传递给Array.prototype.forEach()
,编译器加宽obj
返回到其原始类型,其中不包括A
。现在你和我都知道了forEach()
将立即调用其回调函数,但 TypeScript 不会。就其所知,其价值obj
将在调用回调之前进行修改。并且没有办法tell编译器不同。因此它认为缩小范围不安全并放弃。
所以,解决方法:一个想法是obj
a const
而不是声明它let
:
const obj = { foo: 'hello' };
if (!isA(obj)) throw 'wont ever throw'
Array(5).fill(0).forEach((_, i) => {
obj.bar // This is okay now
});
这实际上并没有改变这样的事实obj.bar
可以在关闭的回调之前添加或删除obj
被称为,但 TypeScript 使用的启发式类似于“const
更有可能是不可变的let
“, 虽然事实并非如此.
类似的解决方法,如果你不能obj
a const
,就是分配一个新的const
缩小范围后的变量,并在回调中使用它:
let obj = { foo: 'hello' };
if (!isA(obj)) throw 'wont ever throw'
const myObj = obj;
Array(5).fill(0).forEach((_, i) => {
myObj.bar // This is okay
});
当然,出于这个原因,您最好跳过obj
完全中间人:
let obj = { foo: 'hello' };
if (!isA(obj)) throw 'wont ever throw'
const bar = obj.bar;
Array(5).fill(0).forEach((_, i) => {
bar // This is okay
});
由你决定。希望有帮助。祝你好运!