你可能不需要 TypeScript 项目引用
如果你曾在大型 TypeScript 代码库或 monorepo 中工作过,你可能很熟悉项目引用。它们确实非常强大。
当你在你的 tsconfig.json
中引用一个项目时,会发生新的事情
- 从引用的项目导入模块将改为加载其输出声明文件 (
.d.ts
) - 如果引用的项目生成一个
outFile
,则输出文件.d.ts
文件的声明将在这个项目中可见 - 运行构建模式 (
tsc -b
) 将自动构建引用的项目(如果它尚未构建但需要构建) - 通过分离成多个项目,你可以大大提高类型检查和编译的速度,减少使用编辑器时的内存使用量,并改进程序逻辑分组的强制执行。
听起来很棒!对吧?!好吧...也许。一旦你向你的项目添加了引用,你现在就需要在你添加或删除包时不断更新它们。这有点糟糕。
好吧...如果你不需要这样做呢?
“内部” TypeScript 包
事实证明,你可能甚至不需要引用,也不需要使用我即将展示的模式的临时 TypeScript 构建步骤,我称之为“内部包”。
“内部包”是一个没有 tsconfig.json
的 TypeScript 包,其 package.json
中的 types
和 main
字段都指向包的未转译入口点(例如 ./src/index.tsx
)。
事实证明,TypeScript 语言服务器 (在 VSCode 中) 和类型检查器可以将原始 .ts
或 .tsx
文件视为其自身的有效类型声明。最后这句话在你读两遍后就很明显了。然而,不太明显的是,你可以将 types
字段直接指向原始源代码。
一旦你这样做,这个包就可以在没有项目引用或 TypeScript 构建步骤(通过 tsc
或 esbuild
等)的情况下使用,只要你遵守 2 条规则
- 内部包的消费应用程序必须转译和类型检查它。
- 你永远不应该将内部包发布到 npm。
据我所知,无论你是否使用 Turborepo 或其他工具,这种内部包模式都适用于所有 yarn/npm/pnpm 工作区实现。我个人已经使用几种不同的元框架测试过这种模式(见下文),但我确信它也适用于其他框架。
Next.js
Next.js 13 可以自动转译和捆绑来自本地包(如 monorepos)或来自外部依赖项 (node_modules
) 的依赖项。
从 Next.js 13.1 开始,你不再需要 next-transpile-modules
包。有关更多信息,请访问 Next.js 内置模块转译 博客文章。
Vite
内部包可以直接工作。无需额外的配置。
React Native
如果你使用 Expo 并使用 expo-yarn-workspaces
或 @turborepo/adapter-expo
包,只要你的目标是 iOS 或 Android,你就可以使用内部包。当你为这些平台运行 Expo 时,所有的 node_modules
都会自动使用 Metro 进行转译。但是,如果你的目标是用于 Web 的 Expo,则内部包将无法工作,因为 node_modules
奇怪地没有为 Web 进行转译。
我向 Expo 团队询问了这种不一致性。他们意识到了这个问题。我被告知这是一个遗留问题。
这种模式的美妙之处
这种模式很棒,因为它为你节省了额外的、不必要的或重复的构建步骤。它还为你提供了项目引用的所有编辑器优势,但无需任何配置。
注意事项
当你使用内部包时,这有点像告诉消费应用程序你拥有另一个源目录——这有利有弊。随着你的消费应用程序的增长,添加更多内部包与向该消费应用程序添加更多源代码相同。因此,当你添加更多源代码时,有更多代码需要转译/捆绑/类型检查...因此这可能会导致消费应用程序的构建速度变慢(因为有更多工作要做),但可能会更快(且更简单)的整体构建时间。当/如果整体构建时间开始受到影响时,你可能会决定将你较大的内部包转换回具有 .d.ts
文件和正常 TypeScript 构建步骤的“常规”包。
如前所述,这种模式实际上与 Turborepo 关系不大。它只是非常非常棒,我认为你应该了解它。由于我们正在积极为 Turborepo 开发预设包构建规则(即“构建器”),我们将使用内部包模式来跳过构建步骤。
说到漫长的构建时间...
这里无耻地推销一下。如果你正在阅读这篇文章,并且你正在与缓慢的构建和测试时间作斗争,我很乐意向你展示 Turborepo 如何提供帮助。我保证 Turborepo 将把你的 monorepo 的构建时间缩短 50% 或更多。你可以在这里申请现场演示。