内容提要
作者分享了 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,但作者坚持用普通构造函数加一些歪门邪道解决。