如何利用 Typescript 提高代码质量(一)
类型也有代数运算,类型也有逻辑。
前言
本文适合以下读者:
- 想了解为什么要使用 Typescript。本文会展示 Typescript 的优势
- 对类型体操的实际应用有怀疑。本文会展示实际业务中常见类型应用
- 想了解 ADT(代数数据类型)。类型也有概念上的加减乘除,这并不是类型体操。
主要内容
- Typescript 的优势(本文)
- 通过集合理解类型
- 自定义类型的集合运算
- 实际业务中常见类型操作应用
- 代数数据类型
- 穷尽枚举
- 代数数据类型拓展
Typescript 的优势
优势是相对而言的,要说优势就是说 Typescript 相对于 Javascript 的优势。那么 Typescript 相对于 Javascript 的优势是什么呢?
最近作者面试一些前端开发人员,都会说 Typescript 是 JavaScript 的超集,提供了静态类型检查,但是仅限于使用 interface 和 type 定义类型,会使用一些简单的 Utility Types。这样回答往往表现的了解不够深入。
有些人可能会说出 interface 和 type 的区别够深入吗、会很多类型体操(例如使用类型进行排序)这样够深入吗、了解distributive conditional types 够深入吗?这确实能体现出你理解的深度,如果确实能应用到实际开发中那才能体现你的优势,这些往往在开发一些工具基础库的时候会使用,平常业务开发几乎不会使用到。
让我们先思考这样一个问题,Typescript 提供了静态类型检查,那我们可以通过静态类型检查来帮我做哪些事情呢?
静态类型检查可以让我们在类型出现错误的时候就会出现编译错误,这样就可以把这部分错误过滤掉,不会出现在运行时。往往运行时再去发现修改要比编译时发现修改成本要高的多,甚至需要在测试阶段发现。
既然如此,我们能不能尽可能的将代码错误或者说业务错误拦截在编译阶段呢?能不能通过定义完类型之后通过编译错误拦截,一步一步修改错误,错误修改完成了,业务逻辑也写完了呢?
如果你了解一些高阶类型的语言例如 Haskell,甚至一些证明助手例如 Coq,你就会发现这些语言的类型系统是非常强大的,可以帮助我们做到这些事情。但是 Typescript 的类型系统并不是很强大而且业务开发中写太多的类型定义也会让人效率低下,实际上我们可以通过一定程度的类型定义来完成编译通过即写完业务代码。这是下一篇文章重点讲解的。
本文主要讨论如何理解类型:
通过集合理解类型
在说这些方法之前,我们先来思考一下什么是类型,我们可以通过集合的角度来思考类型,把类型当作集合、把值当作集合中的元素。如果了解过类型论就知道三个概念:项(可以先理解为变量)、类型、上下文。这三个概念的关系通常可以描述为在某个上下文中某个项是某个类型。例如下面这段代码:
1 | const a: number = 1 |
我们可以描述为在外层的作用域上下文中 a 这个项的类型是 number,在内层的作用域上下文中 a 这个项的类型是 string。
把类型当作集合、把值当作集合中的元素,就没办法把不属于这个集合的元素赋值给属于这个集合的变量。那么考虑一下 Typescript 中的这些类型。
1 | // boolean 这个集合中有两个元素 true 和 false,没法把不属于这个集合的元素赋值给属于这个集合的变量 |
1 这个元素不属于 boolean 这个集合,所以没法把 1 赋值给 boolean 类型的变量。
再看看另一些类型(集合)。
1 | // number 集合里包含所有数值元素,NaN也属于数值元素。 |
如果上述类型不确认的话,可以打开 Typescript Playground 来尝试一下。
现在来思考这样一个问题,如果一个元素属于某个集合A,那么这个元素也属于这个集合A的父集。
1 | const a: unknown = 1 |
因为 unknown 是全集,所以所有元素都可以赋值给 unknown 类型的变量。
我们既然把类型当作集合,那么我们就可以使用集合的运算来描述类型的运算。我们知道常见的集合运算有并集、交集、差集等等。我们可以通过这些集合运算来描述类型的运算。
1 | // 并集运算可以通过联合类型操作符 “|”来实现。 |
可以使用 Typescript Playground 打开上述代码。
自定义类型的集合运算
我们来看一下自定义对象类型的集合运算,我们通过交集来举例,看下面这个例子:
1 |
|
可以用下图描述:
这个类型世界很大很大,我们在这个集合中寻找我们使用的子集。UserWithNameAndEmail 这个集合很大很大,因为除了name和email之外还有其他不确定的特征,这个集合就是一个无穷集合。
后面的文章我们会讨论如何衡量集合(类型)大小,以及代数数据类型是个什么东西。
点赞收藏不迷路,这是我持续创作的最大动力,我们下期见。