Typescript 编译正常,但发出 JavaScript 导入失败

Typescript compiles fine, but emitted JavaScript import fails

我正在尝试通过 Typescript 在网页上使用 Litepicker,但是发出的 JS 无法加载模块。这是我的:

index.html

<html>
    <body>
        <input type="text" id="picker">
    </body>
    <script type="module" src="node_modules/litepicker/dist/litepicker.js"></script>
    <script type="module" src="test.js"></script>
</html>

test.ts

import Litepicker from './node_modules/litepicker/dist/types/index';
new Litepicker({
    element: document.getElementById('picker'),
});

test.ts 编译正常,发出的 test.js 与 test.ts 文件相同。

但是,当我在浏览器上加载 index.html 时,出现以下错误:

GET https://192.168.0.160/test/node_modules/litepicker/dist/types/index net::ERR_ABORTED 404(未找到)test.js:1

如果我像这样从 test.ts 中删除导入:

declare let Litepicker: any;
new Litepicker({
    element: document.getElementById('picker'),
});

然后 emmited JS 在浏览器上运行良好,我没有收到任何错误。

那么,如何在 .ts 上导入模块并且在 .js 上没有错误?

(这是我的 tsconfig.json 文件)

{
    "compilerOptions": {
        "target": "es2020",
        "sourceMap": false,
    }
}

如果依赖项在您的 node_modules 中,您只需通过以下方式导入它:

import Litepicker from 'litepicker';

这也在您链接的他们的文档中

tl;博士

使用捆绑器(示例:Parcel, Rollup, Webpack)。然后你可以这样做:

import Litepicker from 'litepicker';

(并删除 Litepicker 的脚本标签)

问题

test.ts 中,您从 './node_modules/litepicker/dist/types/index' 导入 Litepicker 并使用您的 TypeScript 配置将其编译为:

import Litepicker from './node_modules/litepicker/dist/types/index';

浏览器随后将尝试解析该路径以尝试导入它,但存在许多问题:

浏览器路径解析

浏览器将尝试逐字解析路径。它不会像 Node 那样尝试使用各种文件扩展名解析路径(请参阅伪代码算法 here)。换句话说,它将尝试找到 ./node_modules/litepicker/dist/types/index。并且此文件 (index) 不存在(我假设它存在于 index.d.ts 中,因为它位于名为 types 的目录中)。因此它给你一个 404 not found 错误。

GET https://192.168.0.160/test/node_modules/litepicker/dist/types/index net::ERR_ABORTED 404 (Not Found) test.js:1

导入类型定义与实现

第二个问题是您可能正在导入类型定义文件。我做出这个假设是因为 index 文件所在的目录称为 types。类型在运行时是无用的。浏览器不会理解如何处理它。一般来说,你不需要导入类型定义,你只需要导入实现,因为 TypeScript 会理解类型定义 link 如何与实现同步。

解决方案

您现在可能有不同的想法:

  • 现在我知道浏览器会解析文字路径,我可以更改路径以指向正确的位置,对吗?例如:import Litepicker from 'node_modules/litepicker/dist/litepicker.js'
  • import Litepicker from 'litepicker';是个东西?

导入文字路径的第一个解决方案不符合习惯。它也可能不起作用,具体取决于模块的公开方式。其他开发人员更好地理解使用 import Litepicker from 'litepicker';。但是您在 to another 中提到这样做是行不通的。让我们深入探究原因。

第一个问题现在应该有点明显了,因为我已经在上面详细说明了:浏览器不知道如何解决它。当您观察到错误时,您也发现了这一点:

Error resolving module specifier “litepicker”. Relative module specifiers must start with “./”, “../” or “/”

因此,我们需要帮助浏览器了解此模块的位置。该解决方案的第一部分是模块系统(例如:CommonJS modules, AMD, SystemJS). This essentially augments the native module system (ES modules),提供用于定义模块和连接它们的接口。您可能不会在这里实际实现任何东西,因为这意味着相对深入地了解模块系统并编写大量存在错误风险的样板代码。我建议依赖此解决方案的第二部分:捆绑器。捆绑器将转换您的模块以适应所选的模块系统(此选择可能由捆绑器做出)。捆绑器示例(排名不分先后)包括:

我不会详细说明如何使用捆绑器,因为这取决于您选择的捆绑器,并且捆绑器将提供有关如何使用它的文档。一些打包器可以开箱即用地使用 TypeScript (Parcel),而其他打包器(Rollup 和 Webpack)将需要额外的 configuration/plugins.