[英]Typescript possible code paths vs. typing loophole?
给定代码
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
分支之一的方法,还是存在漏洞仍然允许我传递与两个if
分支都不匹配的兼容对象。
这是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
}
实际上,编译器确实会在传递这些return
语句时缩小v
的类型……它抱怨,但这不是因为它认为当您离开函数底部时v
可能仍然是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
检查的instanceof X
,则可能是这样。 到达此处的另一种标准方法是使用穷举检查辅助函数assertNever()
:
function assertNever(x: never): never {
throw new Error("OH NOES!!");
}
如果调用assertNever()
函数, 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()
已知what()
返回string | never
string | never
代替string | undefined
string | undefined
。 string | never
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
}
好吧,希望有帮助。 祝好运!
我知道使用接口和标记联合的其他版本。 并且此版本可以正确编译,并且所有代码路径都返回一个值。
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});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.