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