【问题标题】:How to correctly require types from dependencies in typescript?如何正确要求 typescript 中依赖项的类型?
【发布时间】:2019-06-27 14:43:24
【问题描述】:

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

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

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

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

【问题讨论】:

  • 我什至开始认为,typescript 通过创建与 JS 包分开的类型声明文件的方法可能是错误的...... Javascript 应该以某些方式(例如,注释)原生支持类型。我开始明白为什么人们仍然可以使用 JSDoc 而不是 TS。
  • 我认为除了@artem 在下面发布的内容之外,真的没有什么好的解决方案。否则,消费者需要直接依赖包。那是因为 typescript 中的枚举会被转译成 JS 代码,如果消费者想使用它,使用源代码是没有区别的。对于接口,我们也许可以重新导出它,但我们也应该询问消费者为什么要使用库依赖中的接口。通常会有更简单的方法不使用它们,而是使用我们库中的 API

标签: typescript npm dependencies yarnpkg


【解决方案1】:

让我们从一个小事开始:如果包 A 需要来自包 C 的东西,那么根据定义,CA 的直接依赖项。

你说你有理由不将C 包含在A 的依赖项中

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

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

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

  2. C 类型没有改变,所以来自C-v1C 类型与C-v2 兼容

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

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

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

你说用这种方法

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

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

如果您的 A 使用来自 C 的类型,那么在使用不同版本的 C 进行版本控制时,您将遇到完全相同的问题。从C 中拆分出类型只会让您提前考虑这些问题,而不是希望事情会“正常工作”。

【讨论】:

  • 嘿@artem 在将类型与源代码分离并手动维护版本方面我也有同样的想法。我想打字稿团队也可能会考虑这个问题,这也是需要DefinetelyTyped/types 的部分原因。很多人只是从依赖项中获取类型而不考虑版本——这对于 Web 应用程序来说可能没问题,但绝对不是库贡献者。在接受答案之前,我想给它更多的时间并听到更多的想法。感谢您的详细解释!
猜你喜欢
  • 2017-09-27
  • 2014-11-29
  • 2021-12-13
  • 2020-01-15
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
  • 2019-10-03
  • 2011-12-24
相关资源
最近更新 更多