你好,我是橙小梓,目前就职于 FreeWheel 核心业务团队,主要负责前端开发工作。我想和你分享一下我在改造现有 JavaScript 项目上的实践经验,手把手带你一起把现有的 JavaScript 项目 TypeScrip 化。
改造背景
先介绍一下改造背景。
TypeScript 作为 JavaScript 的类型化超集,弥补了静态、弱类型的 JavaScript 的缺陷,具有静态类型声明,可以减少不必要的类型判断和人工查看类型的成本,开发过程中进行静态类型检查和类型提示,对提高开发效率有正向作用。基于 TypeScript 的优点和我们面临的现状,FreeWheel 核心业务前端开发团队决定将前端开发语言从 JavaScript 向 TypeScript 切换。
我们改造的项目业务非常复杂,参与开发的人员非常多(代码行数 8 万多行,前端开发人员 40 多人),以及在可预见的将来,项目会有大量的功能迭代。
P.S.:在引入 TypeScript 的时候,我们使用的 TypeScript 版本是 4.2.2。
改造过程
接下来我会从以下 3 个方面来介绍我们是如何把 JavaScript 项目 TypeScrip 化的:
-
迁移方式探讨
-
类型定义公约
-
代码改造实操
-
迁移后的收获
迁移方式探讨
第一部分,我们来探讨一下迁移方式。
在决定把一个 JavaScript 项目 TypeScript 化的时候,首先我们需要去判断我们这个项目的属性以及我们本身具备的一些条件。
如图所示,第一个条件:判断是否一个稳定的组件。这个意思就是在比较久的一段时间内并不会有一些功能迭代进来。在这样的项目下,我们推荐你手动去维护一份.d.ts 的文件,可以做到只是对外提供类型,方便第三方的调用。如果不是一个稳定的组件,我们需要在将来不停地去维护它。
如果有新的功能加进来的话,那我们就来看第二个条件:是否有足够的时间窗口。如果有足够的时间的话,我们推荐使用 TypeScript 来对原来的 JavaScript 代码进行完整的重构。因为 TypeScript 的设计思维和 JavaScript 是不同的,它比较推荐接口设计先于代码实现,所以在很大程度上,原来的 JavaScript 代码是不能匹配这样的设计而写出来的。我们推荐用 TypeScript 重构,这样会比较符合。
如果我们没有足够的时间窗口该怎么办?以我们的项目为例,我们选择的方式是给模块逐个添加类型来达到进行类型约束的目的。当然给模块添加类型也是有顺序的,推荐把底层的模块先加类型,然后再是上层的模块。
类型定义公约
接下来是我们在这个项目中组内约定的类型定义公约。约定这样的类型定义公约目的有两个:第一明确迁移进度,第二更好地做类型约束。
我们约定了一些必须要遵守的公约:
第一条,定义 type 尽量不使用 any。我们都知道,在代码中大量的使用 any 其实可以明显地降低移植难度,但是我们引入 TypeScript 的初衷是为了给代码进行类型约束,那么使用了 any 之后就失去了这种类型约束的作用,所以我们这里把 eslink 中,这一条 no-explicit-any 设置成了 warn,我们并不会完全去禁止使用 any,但是不建议。