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
任务。从 配置任务指南中,我们可以使用传输节点使任务并行运行,同时尊重其他包的源代码更改
然后,使用 turbo check-types
运行您的任务。
最佳实践
使用 tsc
编译您的包
对于内部包,我们建议您尽可能使用 tsc
来编译 TypeScript 库。虽然您可以使用 bundler,但这不是必需的,并且会为您的构建过程增加额外的复杂性。此外,捆绑库可能会在代码到达应用程序的 bundler 之前对其进行修改,从而导致难以调试的问题。
启用跨包边界的跳转到定义
“跳转到定义”是编辑器的一项功能,用于通过单击或热键快速导航到符号(如变量或函数)的原始声明或定义。正确配置 TypeScript 后,您可以轻松地跨内部包导航。
即时包
只要您不使用入口点通配符,从即时包导出的内容将自动将您带到原始的 TypeScript 源代码。跳转到定义将按预期工作。
已编译的包
从已编译的包导出的内容需要使用 declaration
和 declarationMap
配置才能使跳转到定义正常工作。在为该包启用这两个配置后,使用 tsc
编译该包,并打开输出目录以查找声明文件和源映射。
有了这两个文件,您的编辑器现在将导航到原始源代码。
使用 Node.js 子路径导入代替 TypeScript 编译器 paths
可以使用 TypeScript 编译器的 paths
选项在您的包中创建绝对导入,但是在使用 即时包时,这些路径可能会导致编译失败。从 TypeScript 5.4 开始,您可以使用 Node.js 子路径导入来获得更强大的解决方案。
即时包
在即时 (Just-in-Time) 包中,由于不会创建像 dist
这样的构建输出,imports
必须指向包中的源代码。
已编译的包
在已编译包中,imports
指向包的构建输出。
您可能不需要在项目的根目录中放置 tsconfig.json
文件
正如构建存储库指南中所述,您需要将工具中的每个包都视为独立的单元。这意味着每个包都应该有自己的 tsconfig.json
,而不是引用项目根目录中的 tsconfig.json
。遵循此实践将使 Turborepo 更容易缓存您的类型检查任务,从而简化您的配置。
您可能需要在工作区根目录中放置 tsconfig.json
的唯一情况是为不在包中的 TypeScript 文件设置配置。例如,如果您有一个使用 TypeScript 编写的脚本,需要从根目录运行,则可能需要为该文件配置 tsconfig.json
。
然而,这种做法也是不鼓励的,因为工作区根目录中的任何更改都会导致所有任务错过缓存。相反,请将这些脚本移动到存储库中的其他目录。
您可能不需要 TypeScript 项目引用
我们不建议使用 TypeScript 项目引用,因为它们会引入另一个配置点和另一个工作区缓存层。这两个都可能在您的存储库中引起问题,而几乎没有好处,因此我们建议在使用 Turborepo 时避免使用它们。
限制
您的编辑器不会使用包的 TypeScript 版本
tsserver
无法在您的代码编辑器中为不同的包使用不同的 TypeScript 版本。相反,它会发现一个特定的版本并在所有地方使用它。
这可能导致编辑器中显示的 lint 错误与您运行 tsc
脚本检查类型时出现的错误之间存在差异。如果这对您来说是一个问题,请考虑保持 TypeScript 依赖项的版本相同。
包入口点通配符
我们建议显式列出包的入口点 - 但对于某些人来说,这感觉太冗长了。相反,您可以使用通配符来捕获入口点
虽然这会起作用,但它的缺点是无法跨包边界自动导入,这是由于 TypeScript 编译器的性能原因。这个权衡是否值得取决于您的用例。
这有帮助吗?