簡體   English   中英

打字稿可能的代碼路徑與打字漏洞?

[英]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可能仍然是XY 它抱怨是因為它與v的變窄無關地分析代碼路徑,因此它看到一個代碼路徑,該代碼路徑最后隱式返回undefined 解決此問題的唯一方法是確保所有代碼路徑中都有returnthrow

再次注意, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM