发布 TypeScript 包时如何处理可选的对等依赖项?

Pau*_*rts 9 publishing npm typescript

将 TypeScript 包发布到 npm 以提供接受来自一个或另一个对等依赖项的输入的函数时,如何定义可选的对等依赖项?

import { ExternalFoo } from 'foo';
import { ExternalBar } from 'bar';

export const customPackage = (source: ExternalFoo | ExternalBar) => {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

当缺少两个选项之一时,如何防止使用我的包的人出错?

vil*_*asv 8

从 Typescript 3.8 开始,您可以使用以下语法:

import type { ExternalFoo } from "foo";
Run Code Online (Sandbox Code Playgroud)

因此,如果您只是将该库用于类型信息,您可能不必将其列为 adependency或其他optionalDependency。您可能更愿意将其保留为 a,peerDependency以便如果您的用户具有这些依赖项,他们将使用与您的库兼容的版本。当然,添加 asdevDependency也很有用。

导入将d.ts仅存在于生成的文件中,而不存在于.js转译的代码中。但是,一个问题是,如果用户没有安装该库,则该类型会变成这样any,这可能会使您自己的打字变得混乱。例如,如果foo未安装,您的功能将变为

customPackage = (source: any | ExternalBar) =>
// equivalent to customPackage = (source: any) =>
Run Code Online (Sandbox Code Playgroud)

对于这个特定的类型注释,它很糟糕,因为即使我已经bar安装,它也不会用于该类型检查。因此,有一个办法不依赖于该库了,但它并没有解决的困难写作类型注释,不会打破,如果该类型是不存在的

参考

  • 更新:如果您不想强迫消费者使用此选项,您可以将 `// @ts-ignore` 紧接在 `import type` 行之前。拥有“foo”的消费者将看到“ExternalFoo”,没有“foo”的消费者将看到“any”。(我很尴尬:我从来没有向下滚动到最后一个答案来看到这个。抱歉!) (4认同)
  • 需要注意的是,如果使用的代码未指定 `skipLibCheck` TS 编译器选项,则此语法*将*导致错误。 (3认同)

Cod*_*rer 7

结合当前所有答案,这是我为当前版本的 TypeScript(截至 2021 年末)找到的最佳解决方案:

// @ts-ignore -- optional interface, will gracefully degrade to `any` if `foo` isn't installed
import type { Foo } from "foo";
import type { Bar } from "bar";

// Equates to `Bar` when `foo` isn't installed, `Foo | Bar` when it is
type Argument = any extends Foo ? Bar : (Foo | Bar);

export function customPackage(source: Argument): void {
  ...
}
Run Code Online (Sandbox Code Playgroud)

你可以自己尝试一下。如果foo安装了,导出的方法将接受FooBar参数,如果没有安装,则仅接受Bar(not any)。

  • 因此,“node_modules/my-library/some-file.d.ts”在导入语句上方有一个“ts-ignore”注释,但您仍然收到“TS2307”错误?我不知道如何在评论中解决这个问题,但也许你可以在某个地方整理一个最小的重现? (2认同)

uni*_*nal 6

你的情况是目前TypeScript支持不好的情况。

先总结一下你的情况:

  1. foobar您的可选依赖项,这意味着您希望您的消费者将其中之一与您的库一起使用。
  2. 您仅使用这些库中的类型信息,这意味着您没有任何代码依赖性,并且不想将它们添加到dependencies您的package.json
  3. 你的customPackage功能是公开的。

由于第 3 点,您需要在库类型中包含该类型,这意味着您需要添加foobar作为依赖项。这与第1点和第2点相矛盾。

foo如果和的类型bar来自DefinitelyTyped(即来自包@types/fooand @types/bar),那么您可以将它们添加为您的dependenciesin package.json。这样问题就解决了。

如果foo和的类型bar与库本身一起分发,您必须将库包含为dependencies(您不想要的),或者创建类型ExternalFooExternalBar您自己的副本。

这意味着您将不再依赖foobar

另一种方法是仔细查看您的库,看看将foobar作为依赖项包含在内是否有任何危害。根据您的图书馆的性质,它可能没有您想象的那么糟糕。

就我个人而言,我通常会自己声明类型。JavaScript 首先是一种动态语言。