这是 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
}
好的,希望这些帮助。祝你好运!