这是 TypeScript 的设计限制。您可以合理地期望详尽检查将使编译器意识到最后一条if语句之后的代码无法访问。但这不会发生,而且看起来他们也不打算很快解决这个问题。
让我们看看我们自己能做些什么。在以下代码中:
class X { a: string = "a" }
class Y { b: number = 1 }
// does not compile
function what(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
v // Y
if (v instanceof Y) {
return "its a Y";
}
v // never
}
v当您传递这些语句时,编译器实际上确实缩小了的类型......它会抱怨,但不是因为当您从函数底部掉下来时return它认为v可能仍然是 aX或 a 。Y它抱怨是因为它分析代码路径与缩小的隔离v,所以它看到一个undefined在最后隐式返回的代码路径。解决此问题的唯一方法是确保在所有代码路径中都有一个return或。throw
再次注意,v首先缩小到Y,然后缩小到never。修复错误的一种方法是利用缩小到Y,并在那里做一个return而不是第二次if测试:
function what(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
v // Y
return "its a Y";
}
现在没有错误了。如果你相信你的instanceof X支票,这可能是要走的路。另一种标准方法是使用详尽检查辅助函数assertNever():
function assertNever(x: never): never {
throw new Error("OH NOES!!");
}
如果你调用它,该assertNever()函数会引发错误,因此它会返回never. 但它要求它的参数是 type never,所以调用它应该是编译器错误,除非代码不可访问:
function what(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
v // Y
if (v instanceof Y) {
return "its a Y";
}
v // never
return assertNever(v);
}
现在what()已知返回string | never而不是string | undefined. 而且string | never只是string。你可以看到它是如何作为一个详尽检查的,因为如果你不缩小v到它会抛出一个错误never:
function oops(v: X | Y): string {
if (v instanceof X) {
return "its an X";
}
return assertNever(v); // error, v is Y, not never
}
好的,希望这些帮助。祝你好运!