在 TypeScript 中使用聯合類型時,每每會碰到這種尷尬的狀況:typescript
interface Bird {
// 獨有方法
fly();
// 共有方法
layEggs();
}
interface Fish {
// 獨有方法
swim();
// 共有方法
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // 正常
pet.swim(); // ts 報錯
複製代碼
如上所示,getSmallPet 函數中,既能夠返回 Fish 類型的對象,又能夠返回 Bird 類型的對象。因爲返回的對象類型不肯定,因此使用聯合類型對象共有的方法時,一切正常,可是使用聯合類型對象各自獨有的方法時,ts 會報錯。函數
那麼如何解決這個問題呢?最粗暴的方法固然是將聯合類型轉換爲 any,不過這種方法不值得提倡,畢竟咱們寫的是 TypeScript 而不是 AnyScript。ui
此時,咱們使用今天的主角——類型保護,閃亮登場,它能夠完美的解決這個問題。spa
孔乙己說過,茴香豆有四種寫法,同理,實現類型保護,也有四種寫法。code
類型斷言是最經常使用的一種類型保護方法,即直接指定類型。因爲,TypeScript 中識別的類型大可能是靠 TypeScript 的自動類型推算算出來的,因此會出現上面所說的那種問題,即 TypeScript 不知道具體對象類型是什麼,因此不肯定有沒有聯合類型各自獨有的方法。對象
當使用類型斷言直接指定類型時,至關於你讓 TypeScript 開啓了上帝模式,能夠直接知道具體類型是聯合類型中的那個,此時再使用對象的獨有方法就符合 TypeScript 的推斷了。ip
interface Bird {
// 獨有方法
fly();
// 共有方法
layEggs();
}
interface Fish {
// 獨有方法
swim();
// 共有方法
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // 正常
// 經過鴨子類型來進行判斷
if ((pet as Bird).fly) {
// 類型斷言
(pet as Bird).fly()
} else {
// 類型斷言
(pet as Fish).swim()
}
複製代碼
若是嫌棄經過 as 來進行類型斷言不夠上流,還可使用類泛型的寫法,即:原型鏈
let pet = getSmallPet();
pet.layEggs(); // 正常
// 經過鴨子類型來進行判斷
if ((<Bird>pet).fly) {
(<Bird>pet).fly()
} else {
(<Fish>pet).swim()
}
複製代碼
tips:友情提示,雖然使用類泛型寫法進行類型斷言看起來高端一些,可是因爲在 tsx 中語法存在歧義,因此爲了統一塊兒見,推薦使用 as 的方法進行類型斷言。get
在 JS 中,咱們常常使用 in 語法來判斷指定的屬性是否在指定的對象或其原型鏈中。原型
同理,在 TypeScript 中,咱們能夠經過這種方法確認對象類型。
interface Bird {
// 獨有方法
fly();
// 共有方法
layEggs();
}
interface Fish {
// 獨有方法
swim();
// 共有方法
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // 正常
// 使用 in 語法進行類型保護
if ('fly' in pet) {
pet.fly()
} else {
pet.swim()
}
複製代碼
原理同類型斷言同樣,都是引導 TypeScript 的類型推斷,肯定對象類型。
當聯合類型中使用的是 class 而不是 interface 時,instanceof 語法就派上用場了,經過 instanceof 語法能夠區分不一樣的 class 類型。
class Bird {
// 獨有方法
fly() {};
// 共有方法
layEggs() {};
}
class Fish {
// 獨有方法
swim() {};
// 共有方法
layEggs() {};
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // 正常
// 使用 in 語法進行
if (pet instanceof Bird) {
pet.fly()
} else {
pet.swim()
}
複製代碼
typeof 語法不一樣於 in 語法以及 instanceof 語法,in 語法以及 instanceof 語法都是用來引導類型推斷進行不一樣對象類型推斷,而 typeof 語法經常使用於基本類型的推斷(或者是聯合使用基本類型和對象類型)。
簡而言之,當使用 typeof 可以區分聯合類型中的不一樣類型時,便可使用它。
function getSmallPet(): number | string {
// ...
}
let pet = getSmallPet();
if (typeof pet === 'number') {
pet++
} else {
pet = Number(pet) + 1
}
複製代碼
就如茴香豆的四種寫法的本質依然是茴香豆同樣,類型保護的四種寫法的本質也是同樣的,即,引導 TypeScript 中的類型推斷將類型推斷的多選題變爲單選題,這就是類型保護的本質。