拥抱 TypeScript 的历程

拥抱 TypeScript 的历程

💡 原文中文,约2600字,阅读约需7分钟。
📝

内容提要

作者分享了 Mind Elixir 从 JavaScript 迁移到 TypeScript 的经验:先将 strict 设为 false、allowJs 设为 true,逐步添加类型;曾用 d.ts 和 typeRoots 管理全局类型,但打包后用户无法自动获得类型提示;改用 declaration 和 emitDeclarationOnly 输出 d.ts,却因未导出类型而缺失;最终回归 import type,并关闭 allowJs 完成迁移。

🔎

延伸解读

迁移 TypeScript 的渐进式配置策略

作者在迁移初期将 strict 设为 false、allowJs 设为 true,允许 JavaScript 与 TypeScript 共存,从而降低迁移门槛。这种配置让开发者可以逐步将 .js 文件改为 .ts 并添加类型,而不必一次性解决所有类型错误。对于大型 vanilla JavaScript 项目,这种渐进式策略能有效控制迁移风险,避免因严格类型检查导致开发停滞。

全局类型管理的常见误区

作者曾尝试用 d.ts 配合 typeRoots 管理全局类型,通过 /// <reference path> 引入,虽方便但存在严重缺陷:打包后用户无法自动获得 import 的类型提示。因为这种方式仅提供自定义类型,不会让 TypeScript 在导入时自动推断类型。这提醒我们,库的类型声明应确保使用者能获得完整的类型推断,而非仅能手动引用。

声明文件输出的关键细节

使用 declaration 和 emitDeclarationOnly 输出 d.ts 是常见做法,但作者发现,若源文件中类型未导出,生成的 d.ts 会缺失这些类型,导致 IDE 将其视为 any。因此,在编写库代码时,必须确保所有对外暴露的类型都通过 export 导出,否则类型声明文件将不完整,影响使用者体验。

回归 import type 与迁移收尾

经历多种方案后,作者最终回归 import type,并计划关闭 allowJs 完成迁移。import type 能确保类型被正确导出和引用,避免全局类型管理的弊端。此外,作者提到 TypeScript 对构造函数支持不友好,但通过一些技巧仍坚持使用普通构造函数。这反映了迁移中需权衡语言特性与项目习惯。

❓

Q&A

从 JavaScript 迁移到 TypeScript 时,tsconfig.json 中应该先设置哪些选项?

建议先将 strict 设为 false,allowJs 设为 true,这样便于逐步添加类型,并允许 JS 文件共存。

为什么作者认为使用 d.ts 和 typeRoots 管理全局类型是走错了路?

因为这种方案虽然方便全局使用类型,但打包后用户无法自动获得类型提示,import 时不会自动推理出类型。

如何让打包后的库用户能自动获得类型提示?

可以配置 declaration 和 emitDeclarationOnly 输出 d.ts,并在 package.json 中通过 typings 和 exports 字段指定类型入口。

使用 declaration 和 emitDeclarationOnly 输出 d.ts 后,为什么还会出现类型缺失?

因为 d.ts 按源文件结构输出,如果类型没有被 export,生成的 d.ts 中就不会包含这些类型,导致 IDE 将其视为 any。

最终作者如何完成 TypeScript 迁移?

作者回归使用 import type 管理类型,并关闭 allowJs,从而完成迁移。

作者认为 TypeScript 对构造函数支持不友好的地方是什么?

TypeScript 对构造函数的支持不友好,逼人使用 class,但作者坚持用普通构造函数加一些歪门邪道解决。

🏷️

标签

➡️

继续阅读