假設我有以下用戶定義的型別保護函式,用于檢查值是否為上面的數字1000:
function isBigNumber(something: unknown): something is number {
return typeof something === "number" && something > 1000;
}
然后我像這樣使用它:
const strOrNum: string | number = "asdf";
if (isBigNumber(someVar)) {
console.log(someVar * 10); // works because of type-guard
} else {
// here type of strOrNum is "string" and no longer "string | number"
}
我的問題是strOrNumElse 塊中的型別。
看起來型別保護檢查型別并且 TS 也使用型別保護來縮小 Else 塊的型別,在這種情況下這不是我想要的。型別保護是否僅用于檢查型別而沒有傳遞給它們的值的附加資訊?
有沒有這樣的解決方案不改變的回傳型別isBigNumber,以boolean和為檢查的型別,strOrNum再為If陳述句?
uj5u.com熱心網友回復:
沒有>1000型別。如果您正在處理一組有限的、已知的數字,您可以執行類似的操作something is 1001 | 1002 | 1003 | 1004 | 1005,并且 Typescript 會跟蹤它(并且會理解在某種false情況下something仍然可能是 (some other) number),但您不是。
這里的解決方案是“型別標記”,這是一種在 Typescript 的結構型別系統中“偽造”名義型別的有效方法。有很多方法,你可以安裝幾個庫,但為此我將使用我自己的,我只是稱之為As. 我已經As在這個答案的底部包含了一些解釋,但也可以將它用作黑匣子。
有效的方法As是你可以說某事,比如說,,As<"big-number">Typescript 會尊重這一點并跟蹤這個很大的數字。它純粹存在于型別系統中,完全從已編譯的 Javascript 中消失。有了它,你可以撰寫只接受大數字的函式,你可以撰寫確認大數字的打字機,等等。
關于這些品牌的真正重要的事情之一是,如果你有一個說something is X & As<"whatever">,Typescript 會理解它something可能仍然是X,因為它可能是缺少的As<"whatever">部分something。這解決了您的打字機問題。
所以,對于你的例子:
type BigNumber = number & As<"big-number">;
function isBigNumber(something: unknown): something is BigNumber {
return typeof something === "number" && something > 1000;
}
function onBigNumber(value: BigNumber): void {
console.log(value - 1000); // works because BigNumber extends number
}
declare const strOrNum: string | number;
if (isBigNumber(strOrNum)) {
console.log(strOrNum * 10); // works because of type-guard
onBigNumber(strOrNum); // works because of type-guard
} else {
// here type of strOrNum is "string | number"
if (typeof strOrNum === "number") {
console.log(strOrNum * 100); // works because of type-guard
onBigNumber(strOrNum); // ERROR, because strOrNum is number but not As<"big-number">
}
}
的定義 As
declare abstract class As<Tag extends keyof never> {
private static readonly $as$: unique symbol;
private [As.$as$]: Record<Tag, true>;
}
如果你想理解這一點而不是僅僅把它當作一個黑匣子,一些關鍵的注意事項:
declare意味著 TS 不會為這個類生成代碼——它告訴 TS 這個類的運行時 JS 已經存在,我們只是通知 TS 它的存在。在這種情況下,這是一個謊言——這個類根本不存在。abstract阻止我們嘗試new As,因為上面討論的“謊言”會導致運行時錯誤。它不會阻止我們嘗試class X extends As,這會編譯,并且還會導致運行時錯誤。不要那樣做。(不過,declare class X extends As如果你想擴大謊言,你可以。)Tag extends keyof never使As通用,因此我們可以將它與許多不同的品牌一起使用。keyof never是確定與合法 JS 物件鍵 (string | number | symbol)對應的型別聯合的奇怪的最佳實踐。private [As.$as$]告訴 Typescript 這個類的物件有一個私有成員,即它的結構是不同的和特定的。這意味著 Typescript 的結構型別系統會將其視為不同的型別。它是private,所以沒有人可以訪問它(這很好,因為這是另一個謊言),并且 usingAs.$as$被定義為 aunique symbol,保證我們不會與我們將此品牌應用于的任何名稱沖突。Record<Tag, true>因為私有成員的型別“存盤”了Tag傳遞給類的通用引數的任何內容。使它成為一個Record允許多個品牌在同一物件上共存。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/375611.html
上一篇:打字稿中的小寫物件值?
下一篇:打字稿從鍵中獲取物件值
