作者 | Andrey Frolov译者 | 叙缘审校 | 红泥策划 | 闫园园
这个故事发生在很久以前。我们一生中常常押错宝,但不幸的是,我们对此无能为力。因为我们在做决定时,我们总是基于有限的信息(尤其是关于未来的信息),所以本质上我们的决策更像是在赌博。这就是生活:有时发生在你的个人生活中,有时发生在你的工作生活中。但真正重要的是如何从这个决策过程中学到东西。
押注于 Flow 就是这样一个决策。你有可能还不知道 Flow 是什么,它是 Facebook 推出的 JavaScript 超集,为 JavaScript 带来了类型和类型系统。它的设计思路非常清晰:使用一种叫做“Flow”的语法糖编写代码,它会获取你的源代码文件,构建扩展抽象语法树(abstract syntax tree,AST),对其进行分析,如果一切正常,则将其转换为常规的 JavaScript 代码。
我们从 2017/2018 年就开始开发我们的应用程序了,当时 TypeScript 的工具、支持和文档还没有现在那么强大,而且 Flow 和 TypeScript 之间还算势均力敌,同时,项目工程的开发人员更熟悉 Flow,所以我们选择了它。转折点出现在 2019 年左右,TypeScript 开始迅猛发展,Flow 却基本上被抛弃了,因此我们决定在某个时候从 Flow 迁移到 TypeScript。Flow 的使用者们面临下面的问题:
- Flow 有些特性没写文档,只能通过深入 Facebook 的代码库或 Flow 的源代码来了解这些特性。
- 编译过程太慢,会让计算机崩溃。
- 对使用者们遇到的问题,Flow 的维护者们能提供的支持非常有限。
- Flow 放弃了 npm 包的用户端输入模式。
这个列表还可以继续列下去,但受篇幅所限,先列到这里。总之,我们觉得应该回头,换用另一个 JavaScript 超集。如果你问我为什么“另一个 JavaScript 超集”选择的是 TypeScript,下面我分两方面说明下原因:
公司层面的具体原因:
- 我们的后端已经在使用 TypeScript 了,具备相关专业知识和经验。
- 我们在后端程序中遵循约定优于配置原则,可以根据规范自动生成不同客户端的 SDK。
- 我们前端有一个重要组成部分,是基于后端 API 的类似于增删改查(CRUD)的接口。因此,我们更希望将 SDK 用于强类型前端语言。
基于常识的原因:
- TypeScript 有很棒的文档,还有很多教程、书籍、视频和其他学习资源。
- 弱类型系统。这点是有争议的,因为它不像元语言(译注:Meta Language,ML)(比如 Reason、Haskell、Ocaml)的类型系统那样全面,反而因此入门门槛较低,很容易理解清楚。
- 社区支持度高。你能想象到的大多数软件包,TypeScript 都有很好的支持。
迁移从何处开始?
首先添加 tsconfig.json 引入配置:
这个 TypeScript 配置允许我们把 ts 和 tsx 文件添加到系统中。
接下来,再依次启用 ts 各项规则(下图中的选项只是示例——请根据自己的代码库选择适合的选项)。
我们以前用了 Flow,所以我们需要配置一些修复选项,将其添加到.flowconfig 文件中。
准备就绪,现在可以迁移了。走起!等等,就这么简单?
到哪里才算结束?
后来我们碰到了各种各样的问题,搞出来很多 Flow 文件和 TypeScript 文件,使我们陷入了困境。我们花了几个月的时间才明白这么搞是行不通的。
我们的主要问题是,没有制定一个计划,应该从哪些文件开始、花费多少时间和人力、以及如何结合其他关于组织方面或工程方面的事项。
新的一天,新的尝试
我们认清了代码上的问题及导致的原因后,决定再试一次。
还是得祭出关系图来说明问题。请看下面这张图:
任何工程代码都可以通过一张图清晰的展现出来。所以,做法也很简单:从图的叶子开始迁移,一步步向根部移动。在我们的例子中,叶子节点是样式化组件和 redux 层逻辑。
假设你有一个 sum 函数,它不依赖任何东西:
转换为 TypeScript 后,它会变成:
例如,如果你有一个用 JavaScript 编写的简单组件,你马上就能发现 TypeScript 带来的好处。
那么,下面我们就重新开始吧。
第一步是安装 dependency-cruiser。
然后在命令行执行命令:
depcruise --init
或者手动添加.dependency-cruiser.js 配置。下面我展示一个例子,用于对这种迁移进行全面的概述。
把它用起来——这个工具有很多成熟的预设配置和很棒的文档。
工具
Flow to ts——可以将 Flow 源文件原生地转换为 TS 源文件,减少体力劳动。
TS Migrate——可以将 JS 文件转换为 Typescript 文件。这里有一篇很棒的介绍文档。
分析和衡量标准
根据一些实践经验,我们提出了跟踪迁移进度的想法。我们会向其他团队和利益相关者提供透明的信息,以帮助衡量迁移效果,这让整个过程拥有了游戏化的魔力。
在技术层面,我们仅通过手动更新 excel 表格和用 bash 脚本,来计算每次推送到主分支时的 JavaScript/Typescript 文件数,并将结果发布到 Slack 频道。下面是 bash 脚本的基本示例,你可以根据需要修改配置:
给代码库“止血”
迁移是一个过程,一天做不完。大家都非常优秀,但也都有自己的习惯,也会感到疲倦等等。这就是为什么当迁移后,大家还是会习惯性地按旧方式做事情。因此,当工程师继续编写 JavaScript 而不是 Typescript 时,你需要找到一种方法,来给代码库“止血”,即让它停止产生新的需要修补的地方。可以用自动化工具来达成这一目标。最好的办法是编写一个脚本来检查代码库有没有添加新的 JavaScript 文件。第二种办法是使用与测试覆盖相同的思想,将其理念用于测试类型。
这里有一个有用的工具可以满足需求。
设置一个阈值,添加一个 npm 脚本就可以了:
"type-coverage": "typescript-coverage-report -s --threshold=88"
成果
现在我们已经成功地将超过 20 万个 JavaScript 文件迁移到 TypeScript,我们的代码库中已经没有 Flow 或 JS 代码了,而且我们用 TypeScript 用得非常愉快。
查看英文原文: