簡體   English   中英

我怎樣才能使類型謂詞只有在返回 true 時才會變窄?

[英]How can I make a type predicate that only narrows if it returns true?

例如,當且僅當參數是超過10字符的string時, isLongString function 才返回true不能(天真地)實現為類型謂詞,因為它可能會使編譯器混淆以錯誤地縮小范圍。

function isLongString(v: unknown): v is string {
  return typeof v === "string" && v.length > 10;
}

const shortString = "short" as string | number;

if (isLongString(shortString)) {
  const tst: string = shortString;

  // @ts-expect-error
  const tst2: number = shortString;
} else {
  // @ts-expect-error
  const tst: string = shortString;

  // @ts-expect-error
  const tst2: number = shortString;
}

游樂場

對此的近似是使用唯一類型。

function isLongString(v: unknown): v is string & {__limitedPredicate: "isLongString"} {
  return typeof v === "string" && v.length > 10;
}

游樂場

如果謂詞返回true ,則唯一類型的使用幾乎與string相同。 如果謂詞返回false , TypeScript 根本無法縮小類型,因為它只知道v不是唯一類型。

Typescript 的類型系統無法表達像“長度至少為 11 的字符串”這樣的類型,但如果你想要“長度正好為 11 的字符串”,那么你可以這樣寫string & {length: 11} 如果你有這樣的類型,它是兩種類型的交集,那么縮小將(通常)是單向的,因為在else分支中沒有進行縮小,因為不知道兩者中的哪一個相交類型不滿意。 (當然,縮小仍然會出現在像(string & {length: 11}) | number這樣的聯合中。)

在字符串的特定情況下,您可以通過編寫您的類型保護檢查的合適的模板文字類型來解決您的問題。 例如:

function isStringStartingWithFoo(s: unknown): s is `foo${string}` {
    return typeof s === 'string' && s.startsWith('foo');
}

不幸的是,沒有(理智的)方法來使用模板文字類型來描述“長度至少為 11 的字符串”,但是如果您的 function 正在測試比這更具體的內容,那么模板文字類型可能是前進的方向。


在一般情況下,解決方案是編寫一個返回類型,如v is string &...具有交集類型。 ...部分可能是虛構的,如@johncs 的回答。 使用虛構屬性來模擬標稱類型在 Typescript 中是非常標准的,並且不應導致不正確的代碼,因為沒有人有任何理由嘗試訪問不存在的屬性。 但是,您仍然可以改為編寫與字符串長度至少為 11 這一事實相關的內容。

例如,您可以說類型保護檢查它是一個字符串,其match方法在使用正則表達式.{11,}調用時不返回null

type LongString = string & {match(regex: '.{11,}'): RegExpMatchArray}

function isLongString(v: unknown): v is LongString {
  // ...
}

(請注意,這不會阻止調用與其他 arguments 的match ,因為交集類型的函數或方法就像重載。)在這個示例中這有點笨拙,因為您不太可能想調用.match('.{11,}')無論如何都在一個字符串上,但是可以想象它實際有用的例子,也許是一個類型保護來檢查v is MyDataStructure & {isEmpty(): false}或類似的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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