新的ES6语法用于导入CommonJS / AMD模块,即`import foo = require('foo')`。

88

之前我能做到:

import foo = require('foo');

但是现在TypeScript(1.5)支持ES6模块语法,那么在ES6模块语法中实现相同功能的正确方式是什么?


3
这里有一篇相当详细的讨论 http://www.2ality.com/2014/09/es6-modules-final.html 需要翻译成中文。 - user5321531
6个回答

108

正确的方法是继续使用旧的导入语法。新的导入语法仅适用于ES模块,而旧的导入语法适用于ES6之前的模块。这两者是不同的,有意为之。

import * as foo from 'foo' 导入模块 'foo' 的所有属性,它不会将默认值作为foo导入

该功能的设计者说

  • 一个默认导出声明总是声明一个名为default的导出成员,并且始终作为对exports.default的赋值发出。换句话说,export default始终具有ES模块语义。为了与Babel兼容,我们可以选择在模块具有默认导出时发出__esModule标记,但我们实际上不会使用该标记。
  • 一个export =声明,用于替代其他实体以代替模块本身,始终作为对module.exports的赋值发出。在使用export =的模块中有其他导出是错误的。这是现有的TypeScript行为。
  • 使用export =将另一个模块(无论是内部模块还是外部模块)导出到模块的模块可以使用新的ES6结构进行导入。特别地,方便的解构导入可以与这些模块一起使用。使用export =导出另一个模块的模式在提供内部模块的CommonJS / AMD视图的.d.ts文件(例如angular.d.ts)中很常见。
  • 使用export =以非模块实体代替模块本身导出的模块必须使用现有的import x = require("foo")语法导入,就像今天一样。

2016更新: TypeScript编译器在某些情况下开始允许使用import * as foo from 'legacy-module-foo'来获取遗留模块的默认导入。但是,这是一种违反ES6规范的做法(§15.2.1.16"值“*”表示导入请求是目标模块的命名空间对象。”)。

当以这种方式导入的遗留模块被更新为ES6模块时,这些模块的“默认”导入将停止工作(因为* as foo导入应该导入命名空间对象),如果您不知道这样做是TypeScript/SystemJS的hack,可能会非常困惑。未来TypeScript对ES规范的调整也可能导致它们失效。

因此,你应该优先使用上述描述的传统导入语法来加载传统模块,以避免让自己和其他开发人员在代码中工作时对ES6命名空间导入的工作方式感到困惑,并避免混淆性破坏性变化。

2
谢谢。我已经要求澄清:https://github.com/Microsoft/TypeScript/issues/2242#issuecomment-92218146 - basarat
2
这现在已经过时了,对吗?如果我针对ES6目标,则TS会告诉我不允许使用import x = require('foo') - JKillian
4
不,它不过时。在真正的 ES6 模块中,如果你想要导入一个非 ES6 模块(即遗留模块)的默认值,而又不违反 EcmaScript 规范,是没有办法使用 ES6 的 import 语句实现的。如果你想要使用遗留模块,则不能将目标环境设置为 ES6,因为 TypeScript 编译器会在这种情况下直接输出原样的 import 语句,而 ES 规范并没有针对具有默认导出的 CJS/AMD 模块做任何规定。 - C Snover
2
这更加令人困惑,因为对于从ES6/Babel编译的cjs模块,新的import * as <name> from <'module'>语法仍然有效。 - echen
1
@CSnover:“如果你想使用旧版模块,就不能针对ES6环境进行目标设置”-这真的很荒谬...谁不需要使用第三方旧版模块呢?Babel以更合理的方式处理它。 - Teoh Han Hui
显示剩余2条评论

17

ES6 模块语法的相应语法为:

import * as foo from 'foo';

通过名称为foo的本地变量从foo模块中导入所有内容。


6
дҢүз”Ёе“Ғз§ҚеҮәе‡ғиҮ­жі•пәџеҢ“我е°қиҮ•еЏҒдҢүз”Ёexport {Output};ж›үжҚұ旧文件еғ•йѓЁзљ„export = Outputж—¶пәЊе‡ғзҺ°дғ†вЂњresolves to a non-module entity and cannot be imported using this constructвЂқзљ„й”™иҮҮгЂ‚иҮ·её®ж€‘дү®ж”№дёЂдё‹гЂ‚ - dcsan
我必须警告不要用 import * as name from 替换 imports name = require。如果模块是一个 ES6 之前的模块,你会遇到问题。如果有一些初始化代码在模块被需要时启动,那么这些代码可能无法在最新的 Typescript 中运行。我也曾经在升级到 Typescript 2.0.3 后遇到过 typescript 警告,并不得不将我的 import * as 更改为 import require 版本。 - Kristian MT
@user3717718,你能否将这些内容发布到这里的后续问题中:https://stackoverflow.com/questions/54446403/what-is-the-most-forwards-compatible-way-of-importing-commonjs-from-typescript。我一直在想这个问题。听起来import blah = require是最好的选择,但我担心使用非标准JS的东西。 - Max Heiber
不错,但 TypeScript 编译器会生成 require()。我不想在每次编译过程后手动更改它。如何指示 TypeScript 自动为我完成这项任务? - Thomas Weller

15

TypeScript 2.7开始,有一个新的esModuleInterop标志,可以用于启用与CommonJS / AMD / UMD的默认导入。通过在tsconfig.json中将该标志设置为true,此操作应按预期工作:

import foo from 'foo';

在你的tsconfig.json文件中将该标志设置为true。 - Thomas Weller
在“compilerOptions”下。请参见https://www.typescriptlang.org/tsconfig#compilerOptions。 - Danny Guo

3

导入全部,

const foo = require("foo");

如果它是一个文件,那么这将从包“foo”导入所有实例。

const foo = require("./foo");

这样你就可以通过调用 foo.InstanceName 来访问每个实例。

如果你想要导入特定的实例,

import MyInstance from "foo";

这将从 "foo" 导入特定实例(Myinstance)。 您仍然可以使用上述方法导入所有内容,

import * as ReferenceName from "foo";

它的等价于,

const ReferenceName = require("foo");

2
ES6模块实际上是具有新语法的TypeScript外部模块:ES6模块是单独加载的源文件,可能导入其他模块并提供多个外部可访问的导出项。ES6模块具有几种新的导出和导入声明。建议将TypeScript库和应用程序更新为使用新语法,但这不是必需的。据我理解,这意味着鼓励将自己的TypeScript模块迁移到新语法,但仍然使用“import foo = require('foo')”来导入实际的AMD / CommonJS模块。来源

2
那个说法有些误导人,因为它源自于微软。ES6 和 TypeScript 根本没有关系。TypeScript 和 ES6 有相似之处,就像 TypeScript 也有一个类似于预标准模块系统(就像 Angular 1 有一个预标准的模块系统)一样。现在有了一个正式的标准,固守预标准的 TS 模块是愚蠢的。 - pmont

0

另一个选项是使用CommonJS语法导入它:

const foo = require("foo");

TypeScript和Babel都同意对此进行处理。而且,如果你最终编译成ES5或更低版本,那么这与最终形式也不会有太大差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接