给出代码
class X {a:string = "a"}
class Y {b:number = 1}
// does not compile
function what(v: X|Y) {
if (v instanceof X) {
return "its an X";
}
if (v instanceof Y) {
return "its a Y";
}
//missing return
}
打字稿编译器说:
error TS7030: Not all code paths return a value.
是正确的,因为由于结构化键入,我可以将what
称为
let v = {b:1}
what(v)
因为v
在结构上与Y
兼容。现在,我将what
更改为
function what(v: X|Y) {
if ("a" in v) {
return "its an X";
}
if ("b" in v) {
return "its a Y";
}
// missing return
}
,仍然会出现编译错误。
我想知道编译器是否无法得出if
分支之一,还是存在漏洞,仍然允许我传递与两个{ {1}}分支。
答案 0 :(得分:2)
这是TypeScript中的design limitation。您可以合理地期望exhaustiveness checking将使编译器意识到最后一个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
可能仍然是{{ 1}}或X
(当您跌落函数底部时)。它抱怨是因为它分析代码路径与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
,因此除非代码不可访问,否则调用它应该是编译器错误:
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
好的,希望有帮助。祝你好运!
答案 1 :(得分:1)
我知道使用接口和标记联合的其他版本。并且此版本可以正确编译,并且所有代码路径都返回一个值。
interface IX {kind: "a"; a: string }
interface IY {kind: "b"; b: number }
function what2(v: IX|IY): string {
switch (v.kind) {
case "a":
return "its an X";
case "b":
return "its an Y";
}
}
var r1 = what2({kind: "a", a: "hello"});
var r2 = what2({kind: "b", b: 33});