【问题标题】:Create a TypeScript library with optional dependencies resolved by application创建一个 TypeScript 库,其中包含由应用程序解析的可选依赖项
【发布时间】:2022-07-11 14:28:23
【问题描述】:

我编写了一个发布到我的应用程序使用的私有 npm 存储库的库。 该库包含实用程序并依赖于其他库,例如我们选择@aws-sdk/client-lambda。

我的一些应用程序只使用一些实用程序,不需要外部库的依赖项,而一些应用程序使用所有实用程序。

为避免所有应用程序获得大量它们不需要的间接依赖项,我尝试将依赖项声明为 peerDependencies 并让应用程序解析它们需要的依赖项。它可以很好地发布包,并从将 all 的 peerDependencies 声明为他们自己的本地 dependencies 的应用程序中使用它,但是未能声明其中一个依赖项的应用程序在以下情况下会出现构建错误库中包含的.d.ts 文件被导入到应用程序代码中:

error TS2307: Cannot find module '@aws-sdk/client-kms' or its corresponding type declarations.

是否有可能解决这种情况,以便我的库可以包含许多不同的实用程序,但应用程序可能会“挑选”它们在运行时满足这些实用程序要求所需的依赖项? 我必须使用动态导入来执行此操作还是有其他方法?

我尝试在库代码中使用@ts-ignore,它被传播到应用程序导入的d.ts文件,但没有帮助。

设置:

我的图书馆

package.json:

peerDependencies: {
  "@aws-sdk/client-lambda": "^3.27.0"
}

foo.ts:

import {Lambda} from '@aws-sdk/client-lambda';

export function foo(lambda: Lambda): void {
  ...
}

bar.ts:

export function bar(): void {
  ...
}

index.ts:

export * from './foo';
export * from './bar';

my-application1 - 工作正常

package.json:

dependencies: {
  "my-library": "1.0.0",
  "@aws-sdk/client-lambda": "^3.27.0" 
}

test.ts:

import {foo} from 'my-library';

foo();

my-application2 - 无法编译

package.json:

dependencies: {
  "my-library": ...
}

测试:ts:

import {bar} from 'my-library';

bar();

【问题讨论】:

  • 你找到解决这个问题的方法了吗?
  • @Vinay 是的,我做到了。请让我尽快抽出时间写出我的解决方案。
  • 没有完整的文章@JHH,我们可以快速评论一下您找到的解决方案的机制吗?
  • @James 抱歉耽搁了,我写得很快

标签: node.js typescript npm peer-dependencies typescript-module-resolution


【解决方案1】:

我找到了两种处理方法:

1。仅对可选依赖项使用动态导入

如果确保包根文件导出的类型只包含类型和接口,不包含类等,则转译后的 JS 将不会包含任何require 对可选库的声明。然后使用动态导入从函数中导入可选库,以便仅当客户端显式使用库的这些部分时才需要它们。 对于@aws-sdk/client-lambda,这是我的可选依赖项之一,我想公开可以采用Lambda 对象实例或自己创建一个实例的函数:

import {Lambda} from '@aws-sdk/client-lambda';

export function foo(options: {lambda?: Lambda}) { 
  if (!lambda) {
    lambda = new Lambda({ ... });  
  }
  ...
}

由于Lambda 是一个类,它将作为require 语句作为转译JS 的一部分,因此这不能作为可选依赖项。所以我必须 1) 使导入动态和 2) 在我的函数参数中定义一个用于代替 Lambda 的接口,以摆脱包根路径上的 require 语句。不幸的是,在这种特殊情况下,AWS SDK 不提供该类实现的任何类型或接口,因此我不得不提出一个最小类型,例如

export interface AwsClient {
  config: {
    apiVersion: string;
  }
}

...当然,如果缺少代表Lambda 类的类型,您甚至可以求助于any。

然后是动态导入部分:

export async function foo(options: {lambda?: AwsClient}) { 
  if (!lambda) {
    const {Lambda} = await import('@aws-sdk/client-lambda');
    lambda = new Lambda({ ... });  
  }
  ...
}

使用此代码,包的根路径上不再有任何require('@aws-sdk/client-lambda'),只有foo 函数内。只有调用 foo 函数的客户端才必须在他们的 node_modules.

如您所见,这样做的副作用是每个使用可选库的函数都必须是async,因为动态导入返回承诺。在我的情况下,这解决了,但它可能会使事情复杂化。

2。将使用可选依赖项的文件放入包根文件未导出的.ts 文件中(即index.ts)。

这意味着想要使用可选功能的客户必须 通过子路径导入这些文件,例如:

import {OptionalStuff} from 'my-library/dist/optional;

...这显然不太理想。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 2019-11-02
    相关资源
    最近更新 更多