在 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 中的类型推断将类型推断的多选题变为单选题,这就是类型保护的本质。