打字稿:从“时刻”导入*与从“时刻”导入的瞬间

时间:2019-06-08 03:53:44

标签: typescript

我在Typescript方面遇到了奇怪的问题,并导入了moment包。我已经在几个不同的地方看到过它,甚至根据我是否向类中添加了特定的静态方法,甚至在同一文件中看到了它。

问题是这样的:

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

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

我不明白它们之间的区别。我看过其他问题(例如this one,它建议使用第一种语法)。对上面问题的公认答案的评论建议打开allowSyntheticDefaultImports标志,确实可以纠正该问题,但是我有点担心该标志。 The documentation说:

  

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

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

因此,有两个问题:

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

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

1 个答案:

答案 0 :(得分:1)

  1. 不用担心allowSyntheticDefaultImports,这不是您想要的。您需要esModuleInterop

TL; DR打开esModuleInterop并将import * as foo from 'foo'的每个实例替换为import foo from 'foo'

问题源于Node的require太灵活的事实。您可以导出几乎所有内容。由于超出该问题范围的原因,这使得优化结果代码非常困难。

ES定义模块时,它们定义的结构稍微有些结构化。您必须导出一个普通对象。该对象可以具有名为default的属性,但不需要。

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

> const moment = require('moment');
undefined
> typeof moment
'function'
> moment.default
undefined

您会看到moment导出了一个函数,并且它没有名为default的属性。对于TS来说,这是一个难题,其目的是坚持ES标准,但希望支持导入不遵循该标准的老式模块。

如果要编译到较旧的目标,则第一次尝试(也是默认行为)是破坏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属性。令人高兴的是,这意味着导入的东西不必是对象,也可以是函数。因此,TS不仅仅是插入const moment = require('moment'),而且做了一些不同的事情。它创建一个具有名为default的单个属性的对象,并将其设置为等于require('moment'),然后将其返回。

解决同一问题实际上只是一个不同的技巧,但它遵循ES6规范,其行为与Babel的行为更为相似。或者,引用此功能的TS release notes

  

注意:新行为将添加到标志下,以避免不必要的行为   破坏现有的代码库。我们强烈建议将两者都应用于   新的和现有的项目。对于现有项目,名称空间导入   (从* express中导入*为express; express();)必须为   转换为默认导入(从“ express”导入快递;   express();)。