New es6 syntax for importing commonjs / amd modules i.e. `import foo = require('foo')`
Asked Answered
A

6

91

Previously I could do:

import foo = require('foo');

But now that TypeScript (1.5) supports es6 module syntax, what is the correct way to achieve the same in ES6 module syntax.

Ankh answered 13/4, 2015 at 1:39 Comment(1)
There is quite a detailed discussion here 2ality.com/2014/09/es6-modules-final.htmlEthics
M
110

The correct way is to continue using the old import syntax. The new import syntax is for ES modules only, the old import syntax is for pre-ES6 modules. The two are distinct, intentionally so. import * as foo from 'foo' imports all the properties of the module 'foo', it does not import the default value as foo.

From the designer of the feature:

  • An export default declaration always declares an exported member named default and is always emitted as an assignment to exports.default. In other words, export default consistently has ES module semantics. For compatibility with Babel we could optionally emit an __esModule marker when a module has a default export, but we wouldn't actually use that marker for anything.
  • An export = declaration, which substitutes a different entity to be exported in place of the module itself, is always emitted as an assignment to module.exports. It is an error to have other exports in a module that uses export =. This is the existing TypeScript behavior.
  • A module that uses export = to export another module (be that an internal or external module) can be imported using the new ES6 constructs. In particular, the convenient destructuring imports can be used with such modules. The pattern of using export = to export another module is common in .d.ts files that provide a CommonJS/AMD view of an internal module (e.g. angular.d.ts).
  • A module that uses export = to export a non-module entity in place of the module itself must be imported using the existing import x = require("foo") syntax as is the case today.

2016 update: The TypeScript compiler at some point started allowing import * as foo from 'legacy-module-foo' to get the default import of a legacy module in certain circumstances. This is a violation of the ES6 specification (§15.2.1.16, “The value "*" indicates that the import request is for the target module’s namespace object.”).

When legacy modules you import in this manner are updated to ES6 modules, the “default” imports for those modules will stop working (because * as foo imports are supposed to be importing namespace objects), which may be extremely confusing if you don’t know that doing this is a TypeScript/SystemJS hack. It is also possible that a future TypeScript realignment to the ES specification will cause them to break.

As such, you should probably prefer to continue to use the legacy import syntax described above to load legacy modules to avoid confusing yourself and other developers working on your code about how ES6 namespace imports work, and to avoid confusing breaking changes.

Miles answered 13/4, 2015 at 5:28 Comment(7)
Thanks. I've asked for a clarification : github.com/Microsoft/TypeScript/issues/…Ankh
This is out of date now, correct? If I target ES6 it TS tells me that import x = require('foo') is not allowedAnallese
No, it’s not out of date. There is no way to import a default value of a non-ES6 module with an ES6 import in a real ES6 module without violating the EcmaScript specification. If you want to use legacy modules, you can’t target an ES6 environment, since the TypeScript compiler emits the import statements as-is in that case and the ES spec doesn’t have any provision for CJS/AMD modules with default exports.Miles
Thanks for the clarification @CSnover, that makes sense. It does make things tougher though that legacy modules aren't easily compatibleAnallese
This gets even more confusing because for cjs modules that were compiled from ES6/Babel, the new import * as <name> from <'module'> syntax still worksCameliacamella
@CSnover "If you want to use legacy modules, you can’t target an ES6 environment" - Now that's really ridiculous... Who wouldn't need to use third-party legacy modules?! Babel handles it in a much saner way.Rundown
Damn been looking for this answer for days! I've searched everywhere and even in the docs but nothing was indicated why the difference between ES6 and TypeScript import * as and why they worked differently. Thank you!Clearwing
A
19

The corresponding syntax for ES6 module syntax is:

import * as foo from 'foo';

Basically import everything from the foo module into a local variable by the name of foo.

Ankh answered 13/4, 2015 at 1:39 Comment(4)
what is the export syntax that goes with this? I am getting resolves to a non-module entity and cannot be imported using this construct when I tried just export {Output}; at the bottom of my old file to replace export = OutputCider
I must warn against replacing imports name = require with import * as name from. If the module is a pre ES6 module you will get problems. If there are some init code kicking of when the module is required, that may not run with the newest Typescript. I have also experienced typescript warnings after upgrading to Typescript 2.0.3 and had to change my import * as to the import require version.Salomone
@user3717718 can you post this stuff to the follow-up question here: #54446903. I was wondering about that. Sounds like import blah = require is best, but I'm worried about using something that isn't standard JSSextant
Nice, but the TypeScript compiler generates require(). I don't want to change it manually after each compilation process. How do I instruct TypeScript to do it for me automatically?Cosma
B
16

As of TypeScript 2.7, there is a new esModuleInterop flag that can be used to enable default imports with CommonJS/AMD/UMD. By setting that flag to true in your tsconfig.json, this should work as expected:

import foo from 'foo';
Belding answered 13/3, 2018 at 15:26 Comment(2)
"By setting that flag to true in your tsconfig.json" - Where in my tsconfig.json?Cosma
Under "compilerOptions". See typescriptlang.org/tsconfig#compilerOptionsBelding
L
3

to import all ,

const foo = require("foo");

this will import all instance from package "foo" if its a file then

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

so you can access each instance by calling, foo.InstanceName

if you want to import specific instance,

import MyInstance from "foo";

so this will import specific instance (Myinstance) from "foo" you can still import all using above method,

import * as ReferenceName from "foo";

its equivalent to,

const ReferenceName = require("foo");
Leslileslie answered 28/1, 2019 at 10:29 Comment(0)
P
2

ES6 modules are effectively TypeScript external modules with a new syntax: ES6 modules are separately loaded source files that possibly import other modules and provide a number of externally accessible exports. ES6 modules feature several new export and import declarations. It is recommended that TypeScript libraries and applications be updated to use the new syntax, but this is not a requirement.

Source

As far as I understand, that means that you are encouraged to migrate your own TypeScript modules to the new syntax, but keep using import foo = require('foo') for importing actual AMD/CommonJS modules.

Placoid answered 26/12, 2015 at 13:52 Comment(1)
That statement is a bit misleading as it comes from Microsoft. ES6 has nothing to do with Typescript. Typescript has similarities to ES6 in that it also has a pre-standards module system (like how Angular 1 has a pre-standards module system). Now that there is a formal standard, clinging to pre-standards TS modules is foolhardy.Everyman
A
0

Another option is to import it using CommonJS syntax:

const foo = require("foo");

TypeScript and Babel both agree on what to do with this. Also, if you're compiling down to ES5 or less anyway, then this won't be too far off from its final form.

Allometry answered 12/7, 2018 at 20:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.