用于导入 commonjs / amd 模块的新 es6 语法,即 `import foo = require('foo')`

Posted

技术标签:

【中文标题】用于导入 commonjs / amd 模块的新 es6 语法,即 `import foo = require(\'foo\')`【英文标题】:New es6 syntax for importing commonjs / amd modules i.e. `import foo = require('foo')`用于导入 commonjs / amd 模块的新 es6 语法,即 `import foo = require('foo')` 【发布时间】:2015-06-18 06:07:29 【问题描述】:

以前我可以这样做:

import foo = require('foo');

但是现在 TypeScript (1.5) 支持 es6 模块语法,在 ES6 模块语法中实现相同的正确方法是什么。

【问题讨论】:

这里有相当详细的讨论2ality.com/2014/09/es6-modules-final.html 【参考方案1】:

正确的方法是继续使用旧的导入语法。新的导入语法仅适用于 ES 模块,旧的导入语法适用于 ES6 之前的模块。两者是不同的,故意的。 import * as foo from 'foo' 导入模块“foo”的所有属性,它不导入默认值作为foo

From the designer of the feature:

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

2016 年更新: TypeScript 编译器在某些时候开始允许 import * as foo from 'legacy-module-foo' 在某些情况下获取旧模块的默认导入。 这违反了 ES6 规范(§15.2.1.16,“值“*”表示导入请求是针对目标模块的namespace object。”)。

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

因此,您可能更愿意继续使用上述遗留导入语法来加载遗留模块,以避免让您自己和其他开发您的代码的开发人员对 ES6 命名空间导入的工作方式感到困惑,并避免混淆重大更改。

【讨论】:

谢谢。我要求澄清:github.com/Microsoft/TypeScript/issues/… 这已经过时了,对吗?如果我以 ES6 为目标,它 TS 会告诉我 import x = require('foo') 是不允许的 不,它没有过时。在不违反 EcmaScript 规范的情况下,无法在真正的 ES6 模块中导入具有 ES6 import 的非 ES6 模块的默认值。如果你想使用遗留模块,你不能以 ES6 环境为目标,因为在这种情况下,TypeScript 编译器会按原样发出 import 语句,而 ES 规范没有为默认的 CJS/AMD 模块提供任何规定出口。 这变得更加令人困惑,因为对于从 ES6/Babel 编译的 cjs 模块,新的 import * as <name> from <'module'> 语法仍然有效 @CSnover “如果你想使用遗留模块,你不能以 ES6 环境为目标” - 现在这真的很荒谬......谁不需要使用第三方遗留模块?! Babel 以更理智的方式处理它。【参考方案2】:

ES6模块语法对应的语法是:

import * as foo from 'foo';

基本上将 foo 模块中的所有内容导入到名为 foo 的局部变量中。

【讨论】:

与此相关的导出语法是什么?当我尝试在旧文件的底部使用 export Output; 替换 export = Output 时,我得到了 resolves to a non-module entity and cannot be imported using this construct 我必须警告不要将 import name = require 替换为 import * as name from。如果模块是 ES6 之前的模块,你会遇到问题。如果在需要模块时启动了一些初始化代码,则可能无法使用最新的 Typescript 运行。升级到 Typescript 2.0.3 后,我也遇到过 typescript 警告,不得不将我的 import * 更改为 import require 版本。 @user3717718 你能把这些东西发到这里的后续问题:***.com/questions/54446403/…。那时候我想过那个。听起来 import blah = require 是最好的,但我担心使用非标准 JS 的东西【参考方案3】:

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

import foo from 'foo';

【讨论】:

【参考方案4】:

ES6 模块实际上是 TypeScript 外部模块,具有新的 语法:ES6 模块是单独加载的源文件,可能 导入其他模块并提供一些外部可访问的 出口。 ES6 模块具有几个新的导出和导入 声明。建议使用 TypeScript 库和 应用程序被更新以使用新语法,但这不是 要求。

Source

据我了解,这意味着我们鼓励您将自己的 TypeScript 模块迁移到新语法,但继续使用 import foo = require('foo') 来导入实际的 AMD/CommonJS 模块。

【讨论】:

该声明有点误导,因为它来自 Microsoft。 ES6 与 Typescript 无关。 Typescript 与 ES6 的相似之处在于它也有一个预标准模块系统(就像 Angular 1 有一个预标准模块系统一样)。既然有了正式的标准,坚持使用预标准的 TS 模块是很鲁莽的。【参考方案5】:

全部导入,

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");

【讨论】:

【参考方案6】:

另一种选择是使用 CommonJS 语法导入它:

const foo = require("foo");

TypeScript 和 Babel 都同意如何处理这个问题。另外,如果你编译到 ES5 或更少,那么这与它的最终形式不会相差太远。

【讨论】:

以上是关于用于导入 commonjs / amd 模块的新 es6 语法,即 `import foo = require('foo')`的主要内容,如果未能解决你的问题,请参考以下文章

CommonJS,AMD,CMD

CommonJS和AMD/CMD

javascript模块规范:CommonJS,AMD,CMD

记录 script module

AMD规范

amdcmdCommonJS以及ES6模块化