【问题标题】:Typescript compiles fine, but emitted JavaScript import failsTypescript 编译正常,但发出的 JavaScript 导入失败
【发布时间】:2021-11-20 19:41:17
【问题描述】:

我正在尝试通过 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,
    }
}

【问题讨论】:

    标签: typescript import


    【解决方案1】:

    tl;博士

    使用捆绑器(例如:ParcelRollupWebpack)。然后你可以这样做:

    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(未找到)test.js:1

    导入类型定义与实现

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

    解决方案

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

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

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

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

    解析模块说明符“litepicker”时出错。相对模块说明符必须以“./”、“../”或“/”开头

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

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

    【讨论】:

    • 很好的解释!对我帮助很大。谢谢
    【解决方案2】:

    如果依赖项在您的 node_modules 中,您可以简单地通过以下方式导入它:

    import Litepicker from 'litepicker';
    

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

    【讨论】:

    • 我试过了,ts文件编译得很好,但是我在浏览器上得到以下错误:“未捕获的类型错误:解析模块说明符“litepicker”时出错。相对模块说明符必须以“ ./”、“../”或“/”。
    • 删除导入 node_modules 的脚本标签
    猜你喜欢
    • 1970-01-01
    • 2021-10-07
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多