TypeScript
TypeScript 是 monorepos 中的一个出色工具,它允许团队为其 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 库。虽然你可以使用打包器,但这并非必需,并且会给你的构建过程增加额外的复杂性。此外,打包库可能会在代码到达应用程序的打包器之前对其进行混淆,从而导致难以调试的问题。
启用跨包边界的“转到定义”
“转到定义”是一个编辑器功能,可用于通过单击或热键快速导航到符号(如变量或函数)的原始声明或定义。一旦正确配置了 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 版本。相反,它会发现一个特定的版本并在所有地方使用它。
这可能导致你的编辑器中显示的 linting 错误与运行 tsc 脚本检查类型时的错误之间存在差异。如果这对你来说是个问题,请考虑 将 TypeScript 依赖项保持在同一版本。