ModuleImport

Ron Buckton rbuckton at chronicles.org
Fri Jun 20 16:17:45 PDT 2014


> From: John Barton [mailto:johnjbarton at google.com]
> Sent: Friday, June 20, 2014 3:48 PM
> 
> ES6 already has what you want:
> 
> _Named Exports_:
> 
> export var foo = 1;
> 
> _Single Export Object_:
> 
> export var moduleName = {
>   foo: 1,
>   bar: function() {}
> };
> 
> _Single Export Function_:
> 
> export function fName() { }
> 
> 
> And even cooler, the syntax for import is uniform,
> 
> import {foo} from './namedExport';
> 
> import {moduleName} from './singleExportObject';
> 
> import {fName} from './singleExportFunction';
> 

I'm not stating that I specifically "want" anything here, but was rather recommending an alternative approach to the single export vs named export debate and the removal of the ModuleImport production. David's mail was proposing the addition of the following syntax:

```
import * as fs from "fs"
```

This is designed to work around the fact that without ModuleImport, there's no simple way to get the module object for the named exports. What you really want to write is:

```
import fs from "fs";
```

However, the current semantics don't allow this.  David proposed the new syntax as a replacement for ModuleImport. My only issue is that for the end user this could be confusing, and its possibly future-hostile for refactoring.

If I have a library today that uses an object literal as a default export in Node, and I want to migrate to ES6, the easiest approach is to just replace `module.exports =` with `export default`.  My consumers would happy use `import foo from "foo"`. If I later want to move to named exports, I would break my consumers as they would have to change this to `import * as foo from "foo"`.  The whole reason for this is that there is a semantic distinction with how a default export is handled vs. how named exports are handled.

If I were to use TypeScript's syntax for exports and imports, changing from a default export to named exports results in no change for the consumer:

[before.ts]
```
export = {
  foo: 1,
  bar() {}
}
```

[after.ts]
```
export var foo = 1;
export function bar() {}
```

[consumer.ts]
```
import before = require("before");
import after = require("after");
before.foo; // 1
before.bar; // function bar() {}
after.foo // 1
after.bar; // function bar() {}
```

Albeit, TypeScript does not have a Module exotic object, nor does it have mutable bindings, nor an ImportList in its import clause. That said, as far as the consumer is concerned there's no real distinction between the "default export" approach in before.ts and the "named export" approach in after.ts.  We have this distinction in ES6 because it was designed that way to support mutable bindings and cyclic dependencies. I'm proposing that we come up with alternative semantics that preserve that approach while keeping the import syntax simple.

As a module consumer, I would constantly need to be aware of whether I need to use the `import * as foo from "foo"` syntax or the `import foo from "foo"` syntax. Where in Node I would use `require("foo")` for both cases. By changing the semantics of ImportDeclaration in ES6 and using a simpler syntax, we could would save developers the cognitive cost of determining which import syntax to among two very similar forms, as well as supporting the ability for a module author to refactor their module from a default export to named exports for the single-export-as-object case without affecting their consumers.


More information about the es-discuss mailing list