先来看一个例子:
exportinterfaceCat{
coatColor:string;// 毛色
varieties:string;// 品种
weight:number;// 体重meow:()=>void;// 喵喵叫}exportinterfaceDog{
coatColor:string;
varieties:string;
weight:number;bark:()=>void;// 汪汪叫}const getAnimalInfo = isCat ? getCatInfo : getDogInfo;const animal =awaitgetAnimalInfo<Cat | Dog>({name:'kangshifu'});let call;if(isCat){
call = animal.meow;/* 报错,提示:
类型“Cat | Dog”上不存在属性“meow”。
类型“Dog”上不存在属性“meow”。
*/}else{
call = animal.bark;/* 报错,提示:
类型“Cat | Dog”上不存在属性“bark”。
类型“Cat”上不存在属性“bark”。
*/}
该 Demo 由真实案例改编而来
现在我们假设,只能用一个变量来保存可能来自两个不同接口返回的数据,接口返回值类型已经设定为或的关系。
从变量中取某一个类型特有的属性时,便会提示在另一个类型中不存在该属性,应该如何解决?
目前个人已知有三种常用解决方案
1. 在变量取值时进行断言
let call;if(isCat){
call =(animal as Cat).meow;}else{
call =(animal as Dog).bark;}
当 Cat 或 Dog 特有属性较多时,就需要加非常多的断言,会使代码显得十分不优雅
2. 借助类型守卫
什么是类型守卫?
TS 在遇到以下这些条件语句时,会在语句的块级作用域内「收紧」变量的类型,这种类型推断的行为称作类型守卫 (Type Guard)。
- 类型判断:
typeof
- 实例判断:
instanceof
- 属性判断:
in
- 字面量相等判断:
==
、===
、!=
、!==
(这里列举常用的 4 种)
类型守卫可以帮助我们在块级作用域中获得更为精确的变量类型
这里就可以通过判断 animal 中是否存在 meow 属性,来确定变量是否为 Cat 类型的对象
let call;if(isCat &&'meow'in animal){
call = animal.meow;// 鼠标放到 animal 上,可以看到类型被限定为 Cat}elseif(!isCat &&'bark'in animal){
call = animal.bark;// animal 类型被限定为 Dog}
个人并不是很推荐这种方式
真实的开发中,Cat 的特有属性往往有很多,不论使用哪个属性,似乎都在表示我们尝试以偏概全,至少代码看起来是这样的,就会显得十分不优雅
但不得不承认这也是一种非常好的限定类型的方法
‘meow’ 的值是 null 或 undefined 都会不影响 in 的判断,只要存在该属性均会返回 true
3. 将老变量赋值给被限定类型的新变量
let call;if(isCat){const cat: Cat = animal;
call = cat.meow;}else{const dog: Dog = animal;
call = dog.bark;}
个人比较推荐第三种,大家也可以发表一下各自的看法,集思广益
版权归原作者 麦田里的POLO桔 所有, 如有侵权,请联系我们删除。