首页 > 解决方案 > 如何正确要求 typescript 中依赖项的类型?

问题描述

我有一个关于在我的项目中解析类型的问题。所以基本上我有packageA -> packageB-v1 -> packageC-v1,我想使用在 inside 中声明的packageC-v1类型packageA

所有包都是我自己创建的,都是typescript包,通过declaration: true在文件中设置生成声明文件tsconfig.json,它们每个都暴露*.d.ts在他们的dist文件夹中的多个文件。类型没有对应@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 将安装packageC-v2而不是packageC-v1under packageB/node_modules
  2. SomeType首先从packageB-v1by中导出必要的类型export SomeType from 'packageC-v1',然后从 packageA 中导出即可import SomeType from 'packageB-v1'。这也有效,但是,这也意味着要从其消费者可能需要的所有依赖项(可能有很多依赖项)packageB-v1中重新导出所有类型。这通常是不可能的。另外,我听说再导出可能会产生不同的类型,具体取决于每种情况。
  3. packageA'spackage.json文件中,packageC-v1显式添加依赖,即使它实际上并不直接依赖它。所以我们可以使用import SomeType from 'packageC-v1/SomeType. 不幸的是,这也不起作用,因为我们可能有另一个依赖链,例如packageA -> packageD-v1 -> packageC-v2. 在那种情况下,packageC我们应该在哪个版本下安装packageA?这种方法也很糟糕,因为即使 typescript 实际上不会包含packageC在生成的 JS 包中,packageA仅用于使用接口,它可能会为枚举执行此操作。

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

我不确定这些对您是否有意义,并且在这里确实需要一些建议。

标签: typescriptnpmdependenciesyarnpkg

解决方案


让我们从一个琐碎的事情开始:如果一个包A需要来自 package 的东西C,那么C根据定义是A.

你说你有理由不包括C在依赖项中A

我们可能有另一个依赖链,比如 packageA -> packageD-v1 -> packageC-v2

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

  1. AC使用时不需要类型packageD。(顺便说一句,为什么它需要C使用类型packageB呢?)

  2. C类型没有改变,所以C类型 fromC-v1兼容C-v2

对于#1 和#2,我能看到的唯一解决方案是将类型从C单独的包中拆分出来,例如 C-types,并使其成为A.

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

对于每个版本的. C-types_ 我不认为有空文件是一个问题,但如果是这样,你可以发布手动编写的文件(但是我不知道在发布之前对它们进行类型检查,而没有另一个包使用它们)。无需通过DefinedTyped发布并将它们包含在范围内 - 这只是一个约定,而不是要求。您只需要告诉组织中的每个人使用而不是.C.d.ts.jsC.js.d.tsC-types@typesC-types@types/C

你说用这种方法

在版本不匹配、全局声明冲突或名称范围冲突方面,我仍然可以看到这种方法的许多问题(在 DefinetelyTyped/types 方法中也是如此)

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

如果您A使用来自 的类型C,那么使用不同版本的C. 拆分类型C只会让您事先考虑这些问题,而不是希望事情会“正常工作”。


推荐阅读