为什么打字稿允许我导入它在运行时无法使用的依赖项?

2023-12-24

您可以在这里查看我的示例项目:https://github.com/DanKaplanSES/typescript-stub-examples/tree/JavaScript-import-invalid https://github.com/DanKaplanSES/typescript-stub-examples/tree/JavaScript-import-invalid

我创建了这个名为 main.ts 的文件:

import uuid from "uuid";

console.log(uuid.v4());

虽然打字稿适合这种导入,但当我尝试node main.js,它给出了这个错误:

console.log(uuid_1["default"].v4());
                              ^

TypeError: Cannot read property 'v4' of undefined
    at Object.<anonymous> (C:\root\lib\main.js:5:31)
←[90m    at Module._compile (internal/modules/cjs/loader.js:1063:30)←[39m
←[90m    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)←[39m
←[90m    at Module.load (internal/modules/cjs/loader.js:928:32)←[39m
←[90m    at Function.Module._load (internal/modules/cjs/loader.js:769:14)←[39m
←[90m    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)←[39m
←[90m    at internal/main/run_main_module.js:17:47←[39m

如果我将文件更改为此,它会正常执行:

import * as uuid from "uuid";

console.log(uuid.v4());

如果第一个版本无效,为什么打字稿不通知我?

我有一个多文件 tsconfig 设置。检查 github 项目以获取更多详细信息,但以下是可能相关的共享编译器选项:

{
    "compilerOptions": {
        "rootDir": ".",
        "esModuleInterop": true,
        "module": "CommonJS",
        "moduleResolution": "node",
        "composite": true,
        "importHelpers": true,
    },
}

main.js 的外观如下:

不起作用

"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var uuid_1 = tslib_1.__importDefault(require("uuid"));
console.log(uuid_1["default"].v4());

works

"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var uuid = tslib_1.__importStar(require("uuid"));
console.log(uuid.v4());

我对回答自己的赏金问题感到有点内疚,所以我将其标记为社区。我自己写的原因是因为我觉得其他答案确实掩盖了线索。我必须自己进行大量的研究after阅读它才能写出这个。既然如此,我认为我的回答会对那些在我的船上开始的人更有帮助,因为我不知道我不知道什么。我还认为这个问题还有一个额外的解决方案,尽管它会更具侵入性。

我最初的问题是,“如果第一个版本无效,为什么打字稿不通知我?”这是另一个答案的解释:

因为您已启用 esModuleInterop,这也启用了allowSyntheticDefaultImports。 CommonJS 包实际上与该选项不兼容,但 TypeScript 不知道。

这是绝对正确的,但当谈到理解正在发生的事情时,这只是冰山一角:

如果你查看参考文档,它推荐你设置esModuleInterop https://www.typescriptlang.org/tsconfig#esModuleInterop为真。如果它降低了类型安全性,为什么会提出这样的建议呢?嗯,这不是它建议您将其设置为 true 的原因。其实这个设定does not减少类型安全性——它通过修复一些遗留的打字稿错误来提高类型安全性,特别是两个涉及打字稿处理方式的错误requires。您可以阅读文档以获取更多详细信息,但在我看来,如果您使用节点库,我认为将 esModuleInterop 设置为 true 是个好主意。

但! esModuleInterop 有一个副作用。在其文档的最底部,它说:

启用 esModuleInterop 也将启用允许合成默认导入 https://www.typescriptlang.org/tsconfig#allowSyntheticDefaultImports.

呃……有点。 IMO,该文档不正确。它真正应该说的是,“启用 esModuleInterop 将default 允许合成默认导入 https://www.typescriptlang.org/tsconfig#allowSyntheticDefaultImports为 true。”如果您查看allowSyntheticDefaultImports 文档,它会在右侧说明:

嘿,请注意右上角没有说推荐此设置吗?这可能是因为此设置降低了类型安全性:它允许您键入import React from "react";代替import * as React from "react";当模块没有明确指定默认导出时。

通常(即,allowSyntheticDefaultImports 设置为 false),这将是一个错误......因为它是:您不应该能够默认导入模块,除非它具有默认导出。将其设置为 true 会使编译器说:“不,这很好。”

但是,当您将 allowedSyntheticDefaultImports 设置为 true 时,“此标志不会影响 TypeScript 发出的 JavaScript”。这意味着,这个标志让您可以假装该库是在编译时以一种方式编写的,即使事实并非如此。在运行时,这会出错。为什么这个设定会存在?我不知道,但这可能与历史原因有关:

此选项使 TypeScript 的行为与 Babel 保持一致,其中发出额外的代码以使使用模块的默认导出更加符合人体工程学。

似乎曾经(/现在?)有一个时间点,每个人都被认为在使用 Babel。我没有这样做,所以“人体工程学”的好处变成了运行时错误。

作为一种更简洁的方法,您应该使用 import { v4 } from 'uuid'; 导入 uuid;

确实如此,但我认为将allowSyntheticDefaultImports 显式设置为 false 也是一个好主意。它为您提供了更多的类型安全性。不仅如此,它还使得import uuid from "uuid";编译时错误(应该是)。

还有一件事我不明白:

将allowSyntheticDefaultImports 设置为 false 也会使导入类似于import os from "os"; and import _ from "lodash";编译时错误。但当allowSyntheticDefaultImports为true时,这些总是运行良好。一定有一些我遗漏的部分可以解释为什么这些有效,但是uuid没有。

我找不到来源os在我的node_modules中,但我可以看看lodash,及其index.js做这个:

module.exports = require('./lodash');

在该必需文件中,它在底部这样说:

...
/*--------------------------------------------------------------------------*/

  // Export lodash.
  var _ = runInContext();

  // Some AMD build optimizers, like r.js, check for condition patterns like:
  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
    // Expose Lodash on the global object to prevent errors when Lodash is
    // loaded by a script tag in the presence of an AMD loader.
    // See http://requirejs.org/docs/errors.html#mismatch for more details.
    // Use `_.noConflict` to remove Lodash from the global object.
    root._ = _;

    // Define as an anonymous module so, through path mapping, it can be
    // referenced as the "underscore" module.
    define(function() {
      return _;
    });
  }
  // Check for `exports` after `define` in case a build optimizer adds it.
  else if (freeModule) {
    // Export for Node.js.
    (freeModule.exports = _)._ = _;
    // Export for CommonJS support.
    freeExports._ = _;
  }
  else {
    // Export to the global object.
    root._ = _;
  }

我真的不明白这一切在做什么,但我认为这是定义一个名为的全局变量_在运行时?我想这意味着,从打字稿的角度来看,这是巧合的。类型声明文件没有默认值,这通常会导致运行时错误,但几乎巧合的是,这一切最终都会解决,因为 lodash javascript 定义了一个全局变量_? shrug也许这就是一种模式os也使用,但我已经花了足够的时间研究这个,所以我将把它留到另一天/问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么打字稿允许我导入它在运行时无法使用的依赖项? 的相关文章

  • 捕获外部脚本文件中的 javascript 错误

    我有一点 JavaScript Jquery 工具的叠加层 http flowplayer org tools overlay index html 当放到错误使用它的页面上时可能会引发异常 我正在尝试优雅地处理它 我有一个通用的 wind
  • 一个 Next.js 路由中的两个不同子域

    我想使用构建一个新平台Next js https nextjs org React js https reactjs org and 反应路由器 https reacttraining com react router web guides
  • Excel Add In - console.log 在哪里输出它的消息 - NodeJS

    我正在尝试使用 JavaScript API 创建 Excel 插件 但我不明白 console log 在哪里输出它们的消息 所有 Microsoft 文档都包含 console log 示例 但没有解释 console log 输出消息
  • 为什么“dtoa.c”包含这么多代码?

    我将是第一个承认我对低级编程的整体知识有点稀疏的人 我理解许多核心概念 但我不经常使用它们 话虽这么说 我对需要多少代码感到非常惊讶dtoa c http www netlib org fp dtoa c 在过去的几个月里 我一直致力于用
  • JointJS - 处理链接删除点击

    创建链接后 将鼠标悬停在其上会显示红色 X 以将其删除 单击此按钮将触发一系列事件 通过订阅 全部 活动收集 单元格 向下指针 链接 向下指针 cell pointermove x5 似乎可疑 单元格 指针向上 在浏览了文档并花费了太长时间
  • 带有嵌入式 Ruby 的 Javascript:如何安全地将 ruby​​ 值分配给 javascript 变量

    我在页面的 javascript 块中有这一行 res foo 处理这种情况的最佳方法是什么 ruby var里面有单引号吗 否则会破坏 JavaScript 代码 我想我会用红宝石JSON http json org ruby var 上
  • 使用 ES6 模块导出/导入单个类方法?

    假设我有一个像这样的简单课程fileA js class foo constructor x this name x fooMethod x return x hello 我想导入并使用fooMethod in fileB js像这样 im
  • 如何执行密集的 Node.js 计算

    我正在开发一个电子商务应用程序 每次产品从服务器发送到客户端时都必须重新计算产品的定价 每次发送多达数千个产品 计算部分本身相当密集 因为它需要多个数据库查询来计算 我的简单解决方案是将计算部分抽象到另一个专门用于计算的 Node js 应
  • t /= d 是什么意思? Python 和错误

    t current time b begInnIng value c change In value d duration def easeOutQuad swing function x t b c d alert jQuery easi
  • 有没有办法在 TypeScript 2+ 中全局添加类型定义?

    我有一堆简单的 ts files 不是项目 即独立的 ts 脚本 他们使用一些 Node js 功能 TypeScript 和节点类型定义通过安装 npm install g typescript npm install g types n
  • 页面点击其他路径后 $timeout 继续运行

    我在用yo angular fullstack生成器来构建我的网站 当用户注册该网站时 它将发送一封带有链接的激活电子邮件 当用户点击该链接时 会显示激活成功并超时进入主页 但是 当超时未结束 用户点击页面中的任何其他链接时 会跳转到其他页
  • 如何检查jquery数据表中的每个复选框?

    我有一个第一列带有复选框的表格 我使用 jQuery DataTable 插件显示我的表格 我制作了 2 个链接来选择 取消选择每个复选框 这是选择全部的一个 a href Select all a 和 JavaScript functio
  • while 循环元素状态 cypress

    我有一个问题 我想单击一个按钮直到它消失 但次数可能会有所不同 所以我想检查可见性状态 当可见 true时单击按钮 当可见 false时结束测试 但问题是我不知道如何循环从获取元素到末尾的所有链 单击按钮一次 由于中断而停止 如果我删除中断
  • IE6 丢失查询字符串

    我有一个使用 javascript 从查询字符串中获取值的页面window location 从网络服务器运行时效果很好 但如果我通过将其放在地址栏中使用 IE6 在本地运行它 c mysite index htm 网站创建的任何查询字符串
  • 如何在 Angular httpClient 拦截器中使用异步服务

    使用Angular 4 3 1和HttpClient 我需要将异步服务的请求和响应修改为httpClient的HttpInterceptor 修改请求的示例 export class UseAsyncServiceInterceptor i
  • 地址更改时如何停止 Angular 重新加载

    我正在使用 Angular 的scrollTo and anchorScroll像这样 app controller TestCtrl function scope location anchorScroll scope scrollTo
  • 如何在浏览器同步中配置端口

    我有一个gulp任务运行于browser sync 默认情况下它运行在node js服务器的端口3000上 我想将默认端口更改为任何其他端口 例如3010 var gulp require gulp connect require gulp
  • 如何在 jQuery 中检查复选框是否被选中?

    我需要检查checked复选框的属性 并使用 jQuery 根据选中的属性执行操作 例如 如果age复选框被选中 然后我需要显示一个文本框来输入age 否则隐藏文本框 但下面的代码返回false默认情况下 if isAgeSelected
  • jQuery fadeOut 一个 div,fadeIn 另一个 div 在其位置

    我正在尝试一个简单的 jQuery 脚本来淡出一个 div 并淡入另一个 div 但由于某种原因 第一个 div 永远不会淡出 这可能是代码的一个明显问题 但我似乎无法弄清楚 div div div div
  • 从 Node.js 调用 execl、execle、execlp、execv、execvP 或 execvp 的方法

    POSIX 系统公开了一系列exec函数 允许人们将可能不同的东西加载到当前进程中 保留打开的文件描述符 进程标识符等 可以出于多种原因执行此操作 在我的情况下 这是引导 我想更改我自己的进程的命令行选项 然后在现有进程上重新加载它 这样就

随机推荐