如何正确地从打字稿中的依赖项中获取类型?

How to correctly require types from dependencies in typescript?

我对解决项目中的类型有疑问。所以基本上我有 packageA -> packageB-v1 -> packageC-v1,我想在 packageA.

中使用 packageC-v1 中声明的类型

所有包都是我自己创建的,都是通过在tsconfig.json文件中设置declaration: true来生成声明文件的typescript包,它们各自在自己的[中暴露多个*.d.ts文件=16=]文件夹。类型没有相应的 @types/* 包。

这种情况下,应该如何正确导入类型呢?到目前为止我已经尝试过:

  1. import SomeType from 'packageB-v1/node_modules/packageC-v1/dist/SomeType'。这行得通,但我不喜欢 packageA 需要知道 packageC 的安装位置,因为它可能会根据包管理工具(npm/yarn)或安装顺序(见 https://medium.com/learnwithrahul/understanding-npm-dependency-resolution-84a24180901b).我已经看到由于 packageC 的不同版本,类型不一样并且 tsc 不合适的问题。当有另一个依赖项如 packageA -> packageB-v1 -> packageBB-v1 -> packageC-v2 时,可能会发生这种情况,npm 将在 packageB/node_modules.
  2. 下安装 packageC-v2 而不是 packageC-v1
  3. 首先通过 export SomeType from 'packageC-v1'packageB-v1 导出必要的类型 SomeType,然后从 packageA 我可以 import SomeType from 'packageB-v1'。这也有效,但是,这也意味着 packageB-v1 无法从其依赖项的 all 中重新导出 all 类型(可能有很多)它的消费者可能需要。这通常是不可能的。另外,我听说再导出可能会生成不同的类型,具体取决于每种情况。
  4. packageApackage.json文件中,显式添加对packageC-v1的依赖,即使它实际上并不直接依赖它。所以我们可以使用 import SomeType from 'packageC-v1/SomeType。不幸的是,这也行不通,因为我们可能有另一个依赖链,如 packageA -> packageD-v1 -> packageC-v2。在那种情况下,我们应该在 packageA 下安装哪个 packageC 版本?这种方法也很糟糕,因为即使打字稿实际上不会在从 packageA 生成的 JS 包中包含 packageC 仅用于使用接口,它可能会为枚举这样做。

我没有尝试过的最后一种方法是创建我自己的 @types/packageC-v1 并发布它(以及我的其他 ts 包)。但是,如果我为私人组织编写这些包,这意味着我们需要维护一个内部类型存储库,以及维护包的配对版本和与之关联的类型。即使我设法做到了这一点,我仍然可以看到这种方法在版本不匹配、全局声明冲突或名称范围冲突方面的许多问题(在 DefinetelyTyped/types 方法中也是如此)。

我不确定这些对你是否有意义,这里真的需要一些建议。

让我们从一个琐碎的事情开始:如果包 A 需要包 C 中的某些东西,那么根据定义 CA 的直接依赖项。

您说您有理由不将 C 包含在 A

的依赖项中

we may have another dependency chain like packageA -> packageD-v1 -> packageC-v2

在这种情况下,如果 A 使用来自 C-v1 的类型,这应该如何工作?我只能看到两种可能性:

  1. A 在使用 packageD 时不需要来自 C 的类型。 (顺便说一下,为什么它需要 C 类型来使用 packageB 呢?)

  2. C 类型没有改变,因此 C-v1 中的 C 类型与 C-v2

    [=79= 兼容]

对于#1 和#2,我能看到的唯一解决方案是将 C 中的类型拆分到单独的包中,例如 C-types,并使其成为 [= 的开发依赖项10=]。

C-types中的类型应该不是由TypeScript生成的d.ts文件,这些应该是所有接口、类型和枚举(但不是类、类 是一个实现细节,应该保留在 C) 从 C 中移出到包含在单独包中的常规 .ts 文件中。

您必须以与发布 C 相同的方式在 npm 存储库中发布 C-types,为每个版本生成 .d.ts 和空 .js 文件C 个。我不认为有空的 .js 文件是个问题,但如果是的话,你可以只发布手动编写的 .d.ts 文件(但是我不知道在发布之前对它们进行类型检查,没有有另一个包使用它们)。没有必要通过 DefinitelyTyped 发布 C-types 并将它们放在 @types 范围内——这只是一个约定,而不是要求。您只需告诉组织中的每个人使用 C-types 而不是 @types/C.

你说用这个方法

I can still see many issues with this approach in terms of version mismatch, global declaration conflict, or namescope conflict (it is also true in the DefinetelyTyped/types approach)

首先,强烈建议不要使用全局声明 - 模块的发明是有原因的,在任何地方使用的每个名称都应该从某个地方明确导入。

并且如果您的 A 使用来自 C 的类型,您将在使用不同版本的 C 时遇到完全相同的版本控制问题。从 C 中拆分类型只会让你事先考虑这些问题,而不是希望事情会 "just work".