聊聊 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。

此时,我们使用今天的主角——类型保护,闪亮登场,它可以完美的解决这个问题。

孔乙己说过,茴香豆有四种写法,同理,实现类型保护,也有四种写法。

类型断言

类型断言是最常用的一种类型保护方法,即直接指定类型。由于,TypeScript 中识别的类型大多是靠 TypeScript 的自动类型推算算出来的,所以会出现上面所说的那种问题,即 TypeScript 不知道具体对象类型是什么,所以不确定有没有联合类型各自独有的方法。

当使用类型断言直接指定类型时,相当于你让 TypeScript 开启了上帝模式,可以直接知道具体类型是联合类型中的那个,此时再使用对象的独有方法就符合 TypeScript 的推断了。

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 的方法进行类型断言。

in 语法

在 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 的类型推断,确定对象类型。

instanceof 语法

当联合类型中使用的是 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 语法

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

聊聊 TypeScript 中的类型保护的更多相关文章

  1. TypeScript 中枚举类型的理解?应用场景?

    一.是什么 枚举是一个被命名的整型常数的集合,用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型 通俗来说,枚举就是一个对象的所有可能取值的集合 在日常生活中也很常见,例如表 ...

  2. 5种在TypeScript中使用的类型保护

    摘要:在本文中,回顾了TypeScript中几个最有用的类型保护,并通过几个例子来了解它们的实际应用. 本文分享自华为云社区<如何在TypeScript中使用类型保护>,作者:Ocean2 ...

  3. TypeScript Type Compatibility(类型兼容)

    TypeScript中的类型兼容是基于结构归类的.在普通分类的相比之下,结构归类是一种纯粹用于将其成员的类型进行关联的方法.思考下面的代码: interface Named { name: strin ...

  4. TypeScript Type Innference(类型推断)

    在这一节,我们将介绍TypeScript中的类型推断.我们将会讨论类型推断需要在何处用到以及如何推断. 基础 在TypeScript中,在几个没有明确指定类型注释的地方将会使用类型推断来提供类型信息. ...

  5. Typescript中的可索引接口 类类型接口

    /* 5.typeScript中的接口 可索引接口 类类型接口 */ /* 接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用 ...

  6. TypeScript入门-高级类型

    高级类型 交叉类型 交叉类型,就是将多个类型合并为一个新的类型,这个新的类型具有这多个类型的成员,含有这几个类型的所有特性,是他们的综合体,像是集合的并集 例子: function extend< ...

  7. TypeScript 之 基础类型、高级类型

    基础类型:https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Basic%20Types.html 高级类型:https ...

  8. TypeScript完全解读(26课时)_11.TypeScript完全解读-类型推论和兼容性

    11.TypeScript完全解读-类型推论和兼容性 在一些时候省略指令,ts会帮我们推断出省略的类型的地方适合的类型,通过学习ts的类型推论了解ts的推论规则 类型兼容性就是为了适应js灵活的特点, ...

  9. 聊聊 Vue 中 axios 的封装

    聊聊 Vue 中 axios 的封装 axios 是 Vue 官方推荐的一个 HTTP 库,用 axios 官方简介来介绍它,就是: Axios 是一个基于 promise 的 HTTP 库,可以用在 ...

随机推荐

  1. Golang Map实现(一)

    本文学习 Golang 的 Map 数据结构,以及map buckets 的数据组织结构. hash 表是什么 从大学的课本里面,我们学到:hash 表其实就是将key 通过hash算法映射到数组的某 ...

  2. ES6让字符串String增加了哪些好玩的特性呢?

    确实因为现在天气变热了,所以一天天的这么写我也很累.所以如果阅读的时候有什么错误还请大家指出来,不好意思.学习永无止境. OK,今天继续讲解ES6系列知识 学过上一节的解构赋值就知道,ES6确实给我们 ...

  3. Python爬虫篇(代理IP)--lizaza.cn

    在做网络爬虫的过程中经常会遇到请求次数过多无法访问的现象,这种情况下就可以使用代理IP来解决.但是网上的代理IP要么收费,要么没有API接口.秉着能省则省的原则,自己创建一个代理IP库. 废话不多说, ...

  4. 超详细步骤---Linux下的最新Git版本安装

    原文地址:https://blog.csdn.net/u010887744/article/details/53957613 [标注大头] 1.查看当前git版本:git --version 查看最新 ...

  5. python学习24之异常

    '''''''''1.低级错误:纯语法错误2.中级错误:代码存在隐性错误,逻辑缺陷3.高级错误:软件面对不确定性的异常错误''''''一.捕获异常1.基本异常捕获语句try: #异常捕捉语句的开始 代 ...

  6. Zabbix磁盘性能监控

    iostat统计磁盘信息的时候,使用的是/proc/diskstats ,cat /proc/diskstats显示如下 ram0 ram1 ram2 ram3 ram4 ram5 ram6 ram7 ...

  7. vue项目中使用bpmn-基础篇

    内容概述 本系列“vue项目中使用bpmn-xxxx”分为五篇,均为自己使用过程中用到的实例,手工原创,目前属于陆续更新中.主要包括vue项目中bpmn使用实例.应用技巧.基本知识点总结和需要注意事项 ...

  8. Linux系统curl获取公网ip

    收集了几个查询当前公网ip的网址,可以通过curl命令方便的查看 curl cip.cc curl ipinfo.io curl myip.ipip.net curl http://members.3 ...

  9. jenkins及Maven介绍

    一.环境介绍 随着软件开发需求及复杂度的不断提高,团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题.Jenkins自动化部署可以解决集成.测试.部署等重复性的 ...

  10. zabbix管理,添加监控主机

    一:添加本机为监控主机  二.监控其他Linux主机agent端 1.环境部署 [root@localhost ~]# hostname agent.zabbix.com[root@localhost ...