我有一個聯合型別,它部分基于一個陣列——我也想用它來檢查運行時的值。但是,TypeScript 強制我在這里使用型別斷言。考慮下面的代碼:
const Pets = ["dog", "cat"] as const
type Pet = typeof Pets[number]
type Animal = Pet | "tiger"
function checkDanger(animal: Animal) {
if (Pets.includes(animal as Pet)) {
return "not dangerous"
}
return "very dangerous"
}
問題是as Pet部分。
如果我把它排除在外,我會得到: Argument of type 'Animal' is not assignable to parameter of type '"dog" | "cat"'. Type '"tiger"' is not assignable to type '"dog" | "cat"'.
但是,在更復雜的現實世界情況下,該斷言可能會產生我想避免的影響。有沒有辦法在沒有斷言的情況下做這樣的事情?
uj5u.com熱心網友回復:
這是 的一個已知問題includes,請參閱問題/26255。
但是,有一種解決方法。您可以創建自定義curried 型別保護:
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) => (elem: string
): elem is Tuple[number] =>
tuple.includes(elem)
// (elem: string) => elem is "dog" | "cat"
const inPets = inTuple(Pets)
讓我們試試:
const Pets = ["dog", "cat"] as const
type Pet = typeof Pets[number]
type Animal = Pet | "tiger"
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) => (elem: string
): elem is Tuple[number] =>
tuple.includes(elem)
// (elem: string) => elem is "dog" | "cat"
const inPets = inTuple(Pets)
function checkDanger(animal: Animal) {
if (inPets(animal)) {
animal // "dog" | "cat"
return "not dangerous"
}
return "very dangerous"
}
由于您有條件陳述句,我假設可以多載此函式以縮小回傳型別:
const Pets = ["dog", "cat"] as const
type Pet = typeof Pets[number]
type Animal = Pet | "tiger"
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) => (elem: string
): elem is Tuple[number] =>
tuple.includes(elem)
// (elem: string) => elem is "dog" | "cat"
const inPets = inTuple(Pets)
function checkDanger(animal: Pet): "not dangerous"
function checkDanger(animal: Animal): "very dangerous"
function checkDanger(animal: string) {
if (inPets(animal)) {
animal // "dog" | "cat"
return "not dangerous"
}
return "very dangerous"
}
const result = checkDanger('tiger') // very dangerous
const result2 = checkDanger('cat') // not dangerous
Playground 多載簽名的順序很重要。
您可能已經注意到,我的代碼中沒有型別斷言。
inTupletypeguard 有效,因為tuple在函式體內被視為字串陣列。這意味著允許此操作,因為tuple[number]和elem可以相互分配。
const inTuple = <Tuple extends string[]>(
tuple: readonly [...Tuple]) =>
(elem: string): elem is Tuple[number] => {
tuple[2] = elem // ok
elem = tuple[3] // ok
return tuple.includes(elem)
}
uj5u.com熱心網友回復:
不幸的是,這是 typescript 編譯器的限制。但是,您可以使用 Type Guards,而不是在同一行中撰寫數千個函式animal as Pet,如下所示
function isPet(something: Pet | Animal) : something is Pet {
return Pets.includes(something as Pet)
}
function checkDanger(animal: Animal) {
if (isPet(animal)) {
return "not dangerous"
}
return "very dangerous"
}
這種方法更適用,因為如果您要重命名型別定義名稱,重構代碼會更容易。
盡管如此,如果您宣告一個只讀陣列,您將失去靈活性,并且必須執行型別斷言。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/414472.html
標籤:
上一篇:創建元素的正確打字稿型別是什么
