[英]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.