这是MWE:
interface Animals {cat:string, dog:string}
let a: Animals|undefined;
if(a){// if we got `a` from somewhere
[1,2,3].map(v=>a.cat)
// ----------> ~ Object is possibly 'undefined'
}
Playground
我不知道到底是什么错误,它似乎与Map输出的东西或未定义,但为什么a
没有定义?
这是MWE:
interface Animals {cat:string, dog:string}
let a: Animals|undefined;
if(a){// if we got `a` from somewhere
[1,2,3].map(v=>a.cat)
// ----------> ~ Object is possibly 'undefined'
}
Playground
我不知道到底是什么错误,它似乎与Map输出的东西或未定义,但为什么a
没有定义?
2条答案
按热度按时间brccelvz1#
这是TypeScript的一般限制,如microsoft/TypeScript#9998中所述。控制流分析的影响(例如,在
if (a)
的真实性检查之后将a
从Animals | undefined
缩小到Animals
)不会持续到跨函数边界的封闭值。因此,在回调
v => a.cat
的主体内部,一直没有对a
的控制流进行缩小;它被认为是Animals | undefined
类型,因此在对其进行索引时会出现错误。发生这种情况的原因是,编译器目前无法知道回调
v => a.cat
是否立即运行。这是我们所拥有的关于Array.prototype.map()
的带外信息,但从类型系统的Angular 来看,[1,2,3].map
和下面的foo
定义之间没有任何有意义的区别:foo()
函数接受一个回调函数,并在 later 调用它。实际上,当它调用它时,a
* 是 *undefined
。因此,一般来说,编译器抱怨是正确的。你可以希望编译器注意到a
是否被重新分配,如果没有,则抑制错误,但这对编译器来说是一个大量的额外工作。而microsoft/TypeScript#9998的要点是,他们还没有找到任何能够在编译器性能方面为自己带来回报的东西。在microsoft/TypeScript#11498中有一个特性请求,允许类型系统被告知
map()
立即运行其回调,以便任何控制流分析结果都可以在回调中持久化,这将允许map()
和foo()
被区别对待......但目前它还不是语言的一部分。这就是问题所在,编译器不知道回调函数会立即运行,也不会搜索源代码来检查
a
是否被重新分配给了undefined
,所以它会在安全方面出错。目前最好的解决方法是将缩小后的值赋给一个新的
const
变量,根据定义,该变量的类型已经缩小:_a
变量(如果存在)的类型始终为Animals
,而不是Animals | undefined
,因此回调类型检查成功。Playground代码链接
aemubtdh2#
发生这种情况的原因是TypeScript处理变量重新赋值的不确定性的方式。Here is the discussion on design choice。如果您将声明替换为
const
,则错误将在流节中清除,并在初始化时标记它。