TypeScript
TypeScript 是 monorepo 中一个优秀的工具,它允许团队安全地向其 JavaScript 代码添加类型。虽然设置过程有些复杂,但本指南将引导你完成大多数用例中 TypeScript 设置的重要部分。
本指南假设你正在使用最新版本的 TypeScript,并使用了一些仅在这些版本中可用的功能。 如果你无法使用这些版本的功能,可能需要调整本页面的指导。
共享 tsconfig.json
你希望在 TypeScript 配置中构建一致性,以便你的整个仓库可以使用出色的默认设置,并且你的同伴开发人员可以知道在工作区中编写代码时会发生什么。
TypeScript 的 tsconfig.json
设置了 TypeScript 编译器的配置,并具有一个 extends
键,你将使用它在整个工作区中共享配置。
本指南将使用 create-turbo
作为示例。
使用基础 tsconfig
文件
在 packages/typescript-config
内部,你有一些 json
文件,它们代表了你可能希望在各种包中配置 TypeScript 的不同方式。 base.json
文件被工作区中的每个其他 tsconfig.json
文件扩展,如下所示
tsconfig
选项参考
创建包的其余部分
此包中的其他 tsconfig
文件使用 extends
键从基本配置开始,并为特定类型的项目进行自定义,例如 Next.js (nextjs.json
) 和 React 库 (react-library.json
)。
在 package.json
内部,命名包,以便可以在工作区的其余部分中引用它
构建 TypeScript 包
使用配置包
首先,将 @repo/typescript-config
包安装到你的包中
然后,从 @repo/typescript-config
包扩展包的 tsconfig.json
。 在此示例中,web
包是 Next.js 应用程序
创建包的入口点
首先,确保你的代码使用 tsc
进行编译,以便生成 dist
目录。 你将需要一个 build
脚本以及一个 dev
脚本
然后,在 package.json
中设置包的入口点,以便其他包可以使用编译后的代码
以这种方式设置 exports
有几个优点
- 使用
types
字段允许tsserver
使用src
中的代码作为代码类型的真实来源。 你的编辑器将始终使用代码中的最新接口进行更新。 - 你可以快速向你的包添加新的入口点,而无需创建危险的 barrel 文件。
- 你将在编辑器中收到跨包边界导入的自动导入建议。
如果你要发布包,则不能在 types
中使用对源代码的引用,因为只有编译后的代码将发布到 npm。 你需要生成和引用声明文件和源映射。
Lint 代码库
要使用 TypeScript 作为 linter,你可以使用 Turborepo 的缓存和并行化来快速检查整个工作区的类型。
首先,将 check-types
脚本添加到你要检查类型的任何包
然后,在 turbo.json
中创建一个 check-types
任务。 从配置任务指南中,我们可以使任务并行运行,同时使用Transit Node来尊重来自其他包的源代码更改
然后,使用 turbo check-types
运行你的任务。
最佳实践
使用 tsc
编译你的包
对于内部包,我们建议你尽可能使用 tsc
来编译你的 TypeScript 库。 虽然你可以使用 bundler,但这没有必要,并且会为你的构建过程增加额外的复杂性。 此外,捆绑库可能会在代码到达你的应用程序的 bundler 之前对其进行修改,从而导致难以调试的问题。
在包边界之间启用跳转到定义
“跳转到定义”是一个编辑器功能,用于通过单击或热键快速导航到符号(如变量或函数)的原始声明或定义。 正确配置 TypeScript 后,你可以轻松地在内部包之间导航。
即时包
来自即时包的导出将自动将你带到原始 TypeScript 源代码。 跳转到定义将按预期工作。
编译后的包
来自编译后的包的导出需要使用declaration
和 declarationMap
配置才能使跳转到定义工作。 在你为包启用这两个配置后,使用 tsc
编译包,并打开输出目录以查找声明文件和源映射。
有了这两个文件,你的编辑器现在将导航到原始源代码。
使用 Node.js 子路径导入而不是 TypeScript 编译器 paths
可以使用TypeScript 编译器的 paths
选项在包中创建绝对导入,但是当使用即时包时,这些路径可能会导致编译失败。 从 TypeScript 5.4 开始,你可以使用Node.js 子路径导入来获得更强大的解决方案。
即时包
在即时包中,imports
必须以包中的源代码为目标,因为不会创建像 dist
这样的构建输出。
编译后的包
在编译后的包中,imports
以包的构建输出为目标。
你可能不需要在项目的根目录中放置 tsconfig.json
文件
正如构建你的仓库指南中所述,你希望将工具中的每个包都视为其自己的单元。 这意味着每个包都应该有自己的 tsconfig.json
来使用,而不是引用项目根目录中的 tsconfig.json
。 遵循此实践将使 Turborepo 更容易缓存你的类型检查任务,从而简化你的配置。
你可能希望在工作区根目录中放置 tsconfig.json
的唯一情况是为不在包中的 TypeScript 文件设置配置。 例如,如果你有一个使用 TypeScript 编写的脚本,你需要从根目录运行它,你可能需要该文件的 tsconfig.json
。
但是,也不鼓励这种做法,因为工作区根目录中的任何更改都将导致所有任务错过缓存。 相反,将这些脚本移动到仓库中的其他目录。
你可能不需要 TypeScript 项目引用
我们不建议使用 TypeScript 项目引用,因为它们引入了另一个配置点和另一个缓存层到你的工作区。 这两者都可能在你的仓库中引起问题,但收益甚微,因此我们建议在使用 Turborepo 时避免使用它们。
局限性
你的编辑器不会使用包的 TypeScript 版本
tsserver
无法为你的代码编辑器中不同的包使用不同的 TypeScript 版本。 相反,它将发现一个特定版本并在所有地方使用它。
这可能会导致编辑器中显示的 lint 错误与你运行 tsc
脚本来检查类型时显示的 lint 错误之间存在差异。 如果这对你来说是一个问题,请考虑将 TypeScript 依赖项保持在同一版本。