网站搜索

单仓库: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;
}

然而,我不清楚这种类型应该“存放”在哪里。似乎有几个选项:

  1. 把它放在演示文稿 => image-gallery 并将其导入到其他包中
  2. 把它放在模型 => client-store 中,并将其导入到其他包中
  3. 手动创建一个共享的 common-types 包
  4. 创建一个自动生成的共享 common-types 包(组合)

无论你选择哪条路径,你都可能遇到使依赖图复杂化的缺点,从而使构建变为顺序而非并行。此外,并非所有类型都是平等的,有时你希望类型靠近某个组件,有时你希望按语义上下文对类型进行分组。

我想知道是否有我还没想到的出路?

答案#1

众所周知,在 JavaScript(和 TypeScript)中,循环依赖发生在两个模块相互导入时。我想澄清的是,这两个模块可能并不是直接依赖彼此的。例如:

// A wants B from BC
A -> BC
// B wants A from A
BC -> A

请注意,实际的依赖关系并不是循环的。循环依赖只是因为文件/模块结构造成的。在这种情况下,有两种解决方案:

  1. 将常见的依赖项提取到它自己的文件中。
  2. 将依赖循环合并到一个文件中。

在前面的例子中,这对应于:

// 1.
A -> C
B -> A // No circular dependency

// 2.
ABC // No external dependency at all!

在我看来,你的 IImage 是一个可以独立的单元,可以提取到它自己的文件/模块中。根据你的 monorepo 管理工具,你可以将它提取到某个项目中的独立文件中。记住,依赖循环发生在文件之间,而不是项目之间,所以这个解决方案是安全的。

如果你的 monorepo 管理器不允许非库模块之间的依赖,你只能创建一个独立的子项目。

在这两种情况下(关于 monorepo 工具),我都建议后者方法,因为从某种意义上说,它更具未来适应性,将公共代码提取到一个子项目中,这样将来不会导致任何依赖循环。