单仓库:Typescript 及在导入类型时的循环依赖问题
正如在此答案中所见,Typescript 3.8 引入了:
import type
安全导入定义(来源):
import type 仅导入用于类型注解和声明的声明。它总是会被完全删除,因此在运行时不会有任何残留。类似地,export type 仅提供可用于类型上下文的导出,并且也会从 TypeScript 的输出中被删除。
然而,你仍然需要将该包作为依赖项包含。
这可能会导致像我这种情况中的循环依赖。我的monorepo的简要描述是:
client-web:一个由vite驱动的网页客户端(客户端)client-store:一个 redux 存储包(模型)image-gallery:一个图片库包(展示用)
他们都需要注意以下类型:
type IImage = {
id: string;
title: string;
url: string;
urlThumb: string;
}
然而,我不清楚这种类型应该“存放”在哪里。似乎有几个选项:
- 把它放在演示文稿 =>
image-gallery并将其导入到其他包中 - 把它放在模型 =>
client-store中,并将其导入到其他包中 - 手动创建一个共享的
common-types包 - 创建一个自动生成的共享
common-types包(组合)
无论你选择哪条路径,你都可能遇到使依赖图复杂化的缺点,从而使构建变为顺序而非并行。此外,并非所有类型都是平等的,有时你希望类型靠近某个组件,有时你希望按语义上下文对类型进行分组。
我想知道是否有我还没想到的出路?
答案#1
众所周知,在 JavaScript(和 TypeScript)中,循环依赖发生在两个模块相互导入时。我想澄清的是,这两个模块可能并不是直接依赖彼此的。例如:
// A wants B from BC
A -> BC
// B wants A from A
BC -> A
请注意,实际的依赖关系并不是循环的。循环依赖只是因为文件/模块结构造成的。在这种情况下,有两种解决方案:
- 将常见的依赖项提取到它自己的文件中。
- 将依赖循环合并到一个文件中。
在前面的例子中,这对应于:
// 1.
A -> C
B -> A // No circular dependency
// 2.
ABC // No external dependency at all!
在我看来,你的 IImage 是一个可以独立的单元,可以提取到它自己的文件/模块中。根据你的 monorepo 管理工具,你可以将它提取到某个项目中的独立文件中。记住,依赖循环发生在文件之间,而不是项目之间,所以这个解决方案是安全的。
如果你的 monorepo 管理器不允许非库模块之间的依赖,你只能创建一个独立的子项目。
在这两种情况下(关于 monorepo 工具),我都建议后者方法,因为从某种意义上说,它更具未来适应性,将公共代码提取到一个子项目中,这样将来不会导致任何依赖循环。
