打字稿:从“时刻”导入 * 作为时刻与从“时刻”导入时刻

Kry*_*ten 7 typescript

我在使用 Typescript 和导入moment包时遇到了一个奇怪的问题。我在几个不同的地方看到过它,我什至看到它出现在同一个文件中,这取决于我是否向类添加了特定的静态方法。

问题是这样的:

当我使用 导入 moment 时import * as moment from 'moment';,我没有遇到 Typescript 错误,但是我的测试确实遇到了问题;具体来说,他们失败了TypeError: moment is not a function

当我更改导入时import moment from 'moment';,代码有效,但我得到"moment" has no default export.

我不明白这些之间的区别。我已经看到了其他问题(比如这个,它推荐了第一种语法)。对上述问题的已接受答案的评论建议打开allowSyntheticDefaultImports标志,这确实纠正了问题,但我有点担心该标志。文档说:

允许从没有默认导出的模块中默认导入。这不会影响代码发出,只是类型检查。

这似乎意味着没有对以这种方式导入的模块进行类型检查。

所以,两个问题:

  1. allowSyntheticDefaultImports是否正确理解了该标志的 TS 文档?它是否关闭了moment包的类型检查?
  2. 有没有办法(一致地)导入这个包?

FWIW,我正在使用 Typescript 3.4.5 和时刻 2.24.0。

Pac*_*ace 9

  1. 别担心allowSyntheticDefaultImports,这不是你想要的。你要esModuleInterop
  2. 是的

TL; DR开启esModuleInterop和更换的每个实例import * as foo from 'foo'import foo from 'foo'

问题源于 Noderequire过于灵活。您几乎可以导出任何内容。由于超出本问题范围的原因,这使得优化生成的代码变得非常困难。

当 ES 定义模块时,他们定义了一些稍微结构化的东西。您必须导出一个普通对象。这个对象可以有一个命名的属性,default但它不需要。

不幸的是,许多 JS 包没有遵循这个约定。如果我们看moment...

> const moment = require('moment');
undefined
> typeof moment
'function'
> moment.default
undefined
Run Code Online (Sandbox Code Playgroud)

您可以看到moment导出了一个函数,它没有名为default. 对于 TS 来说,这是一个难题,它旨在坚持 ES 标准,但又希望支持导入不符合标准的老式模块。

如果您要编译到较旧的目标并替换import * as foo from 'foo'const foo = require('foo');. 想法是没有人真正需要import * as foo from 'foo',所以这是一种劫持导入样式以支持旧模块的方法。但是它不符合 ES6,因为 的返回值import * as foo from 'foo'应该始终是一个对象,如果我们将其替换为 ,require那么我们可能不会得到一个对象。

不幸的是,这会导致您的代码根据您的目标和构建方式的不同而表现不同。如果您以 ES5 为目标,那么它将回require退到该行为,您将返回一个function. 如果你以 ES6 为目标(这在使用某种打包器时经常发生),那么它会被放入import * as foo from 'foo'并且打包器可能会以不同的方式解释它。他们通常做的是为您提供一个对象,该对象具有default设置为该函数的单个属性。

因此,解决方案是劫持import moment from 'moment'语法。在 ES6 中,它的作用是导入default导出的任何内容的属性。好消息是,这意味着被导入的东西不必是一个对象,也可以是一个函数。因此,与其只是加入const moment = require('moment')TS ,不如做一些不同的事情。它创建一个具有单个属性的对象,default并将其设置为等于require('moment'),然后返回该属性。

这实际上只是解决相同问题的不同技巧,但它遵循 ES6 规范,并且其行为与 Babel 的行为方式更相似。或者,引用有关此功能的TS 发行说明

注意:新行为添加在一个标志下,以避免对现有代码库造成不必要的破坏。我们强烈建议将其应用于新项目和现有项目。对于现有项目,命名空间导入(import * as express from "express"; express();)需要转换为默认导入(import express from "express"; express();)。

  • 谢谢你!这是对解决方案的非常清晰的解释,也许更重要的是,问题从何而来! (2认同)