# 接手 Node 老仓库？这本 TypeScript 书先读第 11 章

> 全书 13 章，能马上用在客户现场的是其中 3 章和 2 个附录。

原文: https://fdetimes.net/zh/books-courses/programming-typescript-client-node-repo/

Boris Cherny 的《Programming TypeScript》共 13 章、7 个附录。但刚拿到客户 Node 仓库权限的前线部署工程师（FDE）不必从第一页读起：第 8、11、12 章，加上附录 D 和附录 F，才是和读懂、修好这份代码最相关的部分。

这本书由 O'Reilly 出版，2019 年 5 月前后上市，副标题是 *Making Your JavaScript Applications Scale*，面向中级 JavaScript 开发者。内容覆盖前端和后端，也讲如何把现有 JavaScript 项目迁移到 TypeScript，并在生产环境中运行。

“现有项目”这几个字，正是 FDE 应该读它的理由。在客户现场，很少有机会从零开始。接手的往往是一个用纯 JavaScript 写的 Node 服务、几个过时的库，外加一个不允许重写的截止日期。

## 从第 11 章读起，而不是第 1 章

第 11 章“Interoperating with JavaScript”讲的是从 JavaScript 逐步迁移到 TypeScript：从加入 TSC 开始，到开启 strict 模式结束。这个顺序，恰好就是 FDE 在别人仓库里该有的做法。

假设客户交给你一个约 40 个 JavaScript 文件的 Node 服务，你要修的 webhook 处理流程分布在其中 3 个文件里。不要把 40 个文件全部迁移。

先加入 TSC 检查现有代码，再只转换必须改动的那 3 个文件。diff 小，客户的工程师才审得过来；风险最高的地方，也先有了正确的类型。

**要点:** 在客户仓库里不要重写，让编译器带着你一次改一个文件。

第一次上手就做这件事：一行代码都不改，直接对仓库运行 TSC。得到的错误清单，就是一张标出代码哪里依赖隐含假设的地图。

## 没有类型的库怎么办？

第 11 章的后半部分分三种情况处理第三方 JavaScript 库。库自带类型声明，直接用；声明在 DefinitelyTyped 上，安装即可；最后一种完全没有声明，时间就耗在这里。

假设客户有一个多年前写的内部 npm 包，用 `module.exports` 导出，公司外从没人给它写过类型。附录 D“Recipes for Writing Declaration Files for Third-Party JavaScript Modules”给出的写法，正好覆盖老 Node 代码常用的 CommonJS 和 UMD 导出方式。

具体做法：第一天就把所有依赖归入这三组。第三组决定了你要花多少小时写声明文件，在承诺交付日期之前，应该把这个数字告诉客户。

## 逐个选项收紧类型，不要一步到位

附录 F“TSC Compiler Flags for Safety”列出了让代码更安全的编译选项。放到客户代码库里，它的价值在于可以逐步收紧，而不是一下开启 strict 模式，冒出几百个错误。

假设开启一个选项就冒出 120 个错误。一个改动 120 处的 pull request，客户的评审者很难痛快批准。一次只开一个选项，每个选项一个 PR，每个 PR 只处理一类错误，客户团队就能看懂你在做什么，你离开后也能接着做下去。

## 错误在客户服务器上爆出来时

第 12 章专门有一节“Running TypeScript on the Server”，还讲了 source map、项目引用（project references）和错误监控。编译后的服务在生产环境抛出异常时，堆栈指向的是生成的 JavaScript 文件。source map 能把你带回对应的 TypeScript 行，读客户日志时就不用靠猜。

第 8 章补齐了 Node 代码缺的那部分：回调、Promise、async/await、事件发射器，以及用子进程实现类型安全的多线程。客户的遗留服务常常把回调和 Promise 混着用，给这些异步边界加上类型，是找出错误在哪里被吞掉的最快办法。

具体做法：修第一个 bug 之前，先确认客户的构建是否生成 source map，错误是否上报到某个监控工具。

## 顺序弄错导致的两个常见错误

第一个错误是第一次上手就开启 strict 模式。第 11 章把 strict 放在最后是有原因的：在从没加过类型的 JavaScript 仓库上开启它，会把一次 3 个文件的修复变成一个客户那边没人愿意审的巨型 PR。

第二个错误是还没把依赖分成三组，就把整个仓库改名为 `.ts`。设想 40 个文件全部转换完，才发现那个内部包没有类型声明。所有调用处都报错，你只能半途停下，按附录 D 去写声明文件。

避开这两个错误的办法，书里已经写了：先给库分类，为第三组写声明，再按附录 F 逐个转换文件、逐个开启选项。

## 谁该读，按什么顺序读

这本书适合 JavaScript 写得扎实、但从没把 TypeScript 引入现有代码库的人。建议顺序：第 11 章、附录 D、附录 F、第 12 章，然后是第 8 章。

讲类型系统的前几章，遇到拿不准的概念时再翻。本书出版于 2019 年，涉及工具链和编译选项的细节，请对照当前的 TypeScript 文档核实。

光读还不够。Boris Cherny 在 GitHub 上有官方的习题答案仓库 `bcherny/programming-typescript-answers`，可以自己先做题再对答案。更好的练习，是严格按照第 11 章的顺序，逐步迁移一个开源 Node 仓库。

从职业发展看，这项技能很容易证明。简历上写“将一个 Node 服务从 JavaScript 增量迁移到 TypeScript，通过一系列 PR 开启 strict 模式，为内部 CommonJS 模块编写类型声明”，远比写“熟悉 TypeScript”有说服力。

设想一则 FDE 招聘启事写着：候选人将直接在客户现有系统中写代码，技术栈为 Node 和 TypeScript。这说明雇主需要能安全修改遗留代码的人，而上面那行简历恰好回应了这一点。把它放在工作经历的最前面，并附上你自己迁移过的仓库链接。

用 FDE 的眼光读目录，这三章和两个附录，就是写给那些要修别人的代码、又不能把它改坏的人的。

**本周动手试试:**

- 找一个纯 JavaScript 的 Node 仓库（自己的或开源的），只加入 TSC 做检查，不改任何文件
- 列出该仓库的全部依赖，分成三组：自带类型声明、在 DefinitelyTyped 上、没有声明
- 自己先做完书中的习题，再对照答案仓库 github.com/bcherny/programming-typescript-answers

## 信源

- [Programming TypeScript. Making Your JavaScript Applications Scale Boris Cherny (Helion)](https://helion.pl/ksiazki/programming-typescript-making-your-javascript-applications-scale-boris-cherny,e_13qs.htm)

- [Programming TypeScript (O'Reilly)](https://i-programmer.info/book-watch-archive/12844-programming-typescript-oreilly.html)

- [bcherny/programming-typescript-answers](https://github.com/bcherny/programming-typescript-answers)
