我对回答自己的赏金问题感到有点内疚,所以我将其标记为社区。我自己写的原因是因为我觉得其他答案确实掩盖了线索。我必须自己进行大量的研究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
也使用,但我已经花了足够的时间研究这个,所以我将把它留到另一天/问题。