我该如何决定将@types/*放在`dependencies`还是`devDependencies`中?

479
我在我的项目中使用TypeScript 2。我想要使用一些js库,但是也需要这个库的typings。我可以通过简单的npm install @types/some-library安装类型声明文件。我不确定我应该将它们添加到--save还是--save-dev。似乎DefinitelyTyped的GitHub readme都提到了这两种版本,但从未解释过。我认为@types应该放在devDependencies中,因为类型声明只是用于开发而不在运行时使用,但是我看到很多次@types被放在dependencies中。我感到困惑。

我应该如何决定 @types/* 放在dependencies还是devDependencies中呢?是否有更正式的说明呢?


你是在生成一个捆绑包还是一个将被他人使用的软件包?在我看来,只有在后一种情况下,你才需要区分 dependenciesdevDependencies - Valentin
1
我从头开始用js/ts制作了一些游戏。我使用webpack打包所有内容。目前没有后端,但有可能将其全部包装在Electron中,使其成为独立的应用程序。我不认为任何人会将其用作自己应用程序的依赖项,但我想这是可能的(想想GTA游戏中的迷你游戏;而我的游戏是开源的)。尽管如此,我仍然希望学习和遵循最佳实践,这也是我制作这个游戏的主要原因。希望我已经足够清楚地阐述了我的用例。 :) - kamyl
1
是的,这很有道理,只是想确认一下我的原始答案是否与你的用例相关。我仍然认为,在构建捆绑包时,“devDependencies”和“dependencies”的区别是无关紧要的,这也是“create-react-app”所强制执行的同样,但最终选择权在于您。 - Valentin
5个回答

336

假设你正在开发一个名为“A”的包,其中在devDependencies中有一个@types/some-module包。出于某种原因,你要从@types/some-module导出这个类型:

import { SomeType } from 'some-module';

export default class APackageClass {
  constructor(private config: SomeType) {
    // …       
  }
}

现在,使用 TypeScript 的“A”包的消费者无法猜测SomeType是什么,因为未安装“A”包的devDependencies

在这种特殊情况下,您需要@types/*包与常规dependencies一起放置。对于其他情况,devDependencies就足够了。


20
你的意思是,如果我在实现中只使用某个类型,那么它的类型定义可以被视为“devDependencies”吗? - Franklin Yu
29
是的,@FranklinYu。一旦类型出现在声明文件中,您需要将其放置在“dependencies”中。否则,“devDependencies”也可以。 - wookieb
9
一个包适用于TS和JS。JS开发者不需要这些类型来编译他们的代码。将类型定义添加到“dependencies”中会使依赖树臃肿。 - Tyler Liu
5
没错。虽然不算完美,但这就是现实。你也可以使用“optionalDependencies”,但我认为在大规模应用时可能会非常麻烦。 - wookieb
7
如果我正在开发一款终端用户应用程序而不是包,那么我可以将所有的 @types/.. 移动到 devDependencies 中,对吗? - Giovanni Patruno
显示剩余5条评论

93
如果你只是要生成一个打包文件,那么可能没有必要区分 `dependencies` 和 `devDependencies`。`npm` 的这个特性通常在发布一个可以被他人使用的包时有用,你不想向他们发送冗余的依赖项。
除非你有紧迫的需要,否则除了这种情况外,拆分依赖关系可能对你没有帮助,我的建议是选择其中一个并把所有东西都放在那里。如果需要,之后也不难将它们拆分开来。
这种做法在现实生活中的一个著名例子是 `create-react-app`,默认情况下,它创建的未弹出的样板文件将所有内容放在 `dependencies` 中,参见 这个线程这个答案

13
如果您不发布该软件包,那么就没问题了。但如果您要发布它,那就与开发和运行时无关,而是与“构建此软件包所需的内容”和“使用此软件包所需的内容”有关。 - Yogu
1
@Yogu 这就是我一开始做出区分的原因,所以我完全同意你的观点。 - Valentin
47
我不同意这个建议。当你使用 npm install --production 或者 npm ci --production 命令的时候,devDependencies 不会被安装,因此在运行生产环境代码时也不可用。对于服务而言,这是一个非常重要的区别,而不仅仅是针对库。 - Brad Wilson
3
@BradWilson 你说得对,有很多种npm工作流程,如果你的使用情况需要区分,那么一定要这样做。请随意提供你自己对这个困境的答案。 - Valentin
1
我已经更新了我的答案,提到了其他使用情况的存在,其中区别可能是有意义的,并给出了现实世界的例子。感谢您的反馈! - Valentin
显示剩余2条评论

45
在部署Node.js应用程序到生产环境的特定情况下,我们希望只安装运行该应用程序所需的依赖项。
可以使用以下命令:
npm install --omit=dev 或
npm ci --production 或
yarn --production
在这种情况下,类型应该在devDependencies中,以防止它们使安装过程变得臃肿。
(为避免误解,在构建应用程序的机器上不应使用--production或--omit=dev选项,否则TypeScript编译器会发出警告。)
备注:我知道Brad Wilson在另一个答案的评论中提到了这一点。不过,这个观点似乎值得成为一个答案。

1
我正在开发一个使用第三方库的应用程序,将所有@types/*包放在devDependencies中,但是生产构建失败,因为TypeScript在生产构建中寻找这些类型。 - omerts
@omerts:当你写“在生产构建中寻找这些类型”时,你是指“在编译期间在package.json中寻找这些类型”吗?我问这个问题是因为生产构建是在目标机器上运行的,如果TypeScript编译器在那个阶段被调用,那将会很奇怪。另一方面,如果你是在谈论开发者机器上的编译,那么devDependencies应该仍然存在于那个阶段,因为--production只应该在安装到生产环境时使用,而不是在开发者机器上使用。 - Carsten Führmann
当在CI或Docker环境中使用生产标志运行yarn/npm install,然后运行yarn build时,由于缺少类型,TypeScript编译器将失败。 - omerts
问题似乎在于您已经在构建应用程序的机器上使用了 --production 选项。相反,该选项应仅在运行应用程序的机器上使用,而不是在构建时使用。我在这方面澄清了我的主要答案。 - Carsten Führmann
感谢你的解释,为什么要在只运行应用程序的机器上运行npm / yarn install? - omerts
2
@omerts 有时候从构建机器复制节点模块到主机(即运行应用程序的机器)是可能的,但这似乎是一个可疑的做法,因为:即使主机上的 Node.js 版本号与构建机器相同,目标机器可能是不同的操作系统。例如,您可能在 Windows 上开发,但在 Linux 上运行。而且有些情况下,例如在 Windows 上执行 npm install 的输出在 Linux 上不正确。 - Carsten Führmann

1
其他答案很有道理,但我想补充一点,即同行依赖的类型声明包也应该放在dependencies而不是peerDependencies中。假设ba的插件。而c使用了ab。为什么不应该将@types/a放在bpeerDependencies中?如果bpackage.json像这样:
{
  "peerDependencies": {
    "a": "1.5.x"
    "@types/a": "1.4.x"
  }
}

c只能使用@types/a@1.2.x中定义的接口,但c被强制安装@types/a@1.4.x

此外,c可能是一个普通的JavaScript包而不是TypeScript包,但c也被强制安装@types/a@1.4.x

因此,b的正确package.json应该如下:

{
  "peerDependencies": {
    "a": "1.5.x"
  },
  "dependencies": {
    "@types/a": "1.4.x"
  }
}

1
根据你的代码将用于什么目的。如果你的生产代码需要这些类型(例如:如果你创建了一个自定义包来重新导出这些类型),那么它们应该放在“dependencies”中;否则,如果只是用于开发目的,应该放在“devDependencies”中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接