ES6 Modules #2242

Closed
ahejlsberg opened this Issue Mar 7, 2015 · 72 comments

Comments

Projects
None yet
@ahejlsberg
Member

ahejlsberg commented Mar 7, 2015

This issue describes TypeScript's support for ECMAScript 6 modules as implemented in #1983, #2197, and #2460.

TypeScript 1.5 supports ECMAScript 6 (ES6) modules. 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. The new ES6 module syntax coexists with TypeScript's original internal and external module constructs and the constructs can be mixed and matched at will.

In TypeScript 1.5, a source file is considered an external module if it contains at least one of the following:

  • A top-level declaration that specifies an export modifier.
  • An new ES6 export or import declaration of any form.
  • An original TypeScript export-equals assignment of the form export = Point.
  • An original TypeScript import-equals statement of the form import Math = require("math").

An external module has a set of exports that are specified using various forms of export declarations. Those exports can be imported into local name bindings in other modules using various forms of import declarations.

An external module may designate a default export, which is an export with the reserved name default. A number of short-hand export and import declaration constructs exist to facilitate easy export and import of the default entity.

For backwards compatibility with CommonJS and AMD style modules, TypeScript also supports export-equals declarations of the form export = Point. Unlike default export declarations, which are just shorthand for an export named default, export-equals declarations designate an entity to be exported in place of the actual module.

As ES6 modules gain adoption, TypeScript's original export-equals and import-equals declarations are expected to become legacy.

Export Declarations

When a declaration specifies an export modifier, each declared name is exported from the containing module exactly as is the case with original TypeScript external modules. For example:

export interface Stream { ... }
export function write(stream: Stream, data: string) { ... }

Module members can also be exported using separate export declarations, and such declarations can specify different names for exports using as clauses. For example:

interface Stream { ... }
function writeToStream(stream: Stream, data: string) { ... }
export { Stream, writeToStream as write };  // writeToStream exported as write

An export declaration exports all meanings of a name. For example:

interface Stream { ... }
function Stream(url: string): Stream { ... }
export { Stream };  // Exports both interface and function

Re-exporting

An export declaration that specifies a from clause is a re-export. A re-export copies the exports of a given module to the current module without introducing local names.

export { read, write, standardOutput as stdout } from "./inout";

An export * declaration can be used to re-export all exports of another module. This is useful for creating modules that aggregate the exports of several other modules.

export function transform(s: string): string { ... }
export * from "./mod1";
export * from "./mod2";

An export * doesn't re-export default exports or exports with names that are already exported from the current module. For example, the transform export in the module above hides any transform export in the re-exported modules.

Default Export

An export default declaration specifies an expression that becomes the default export of a module:

export default {
    name: "hello",
    count: 42
};

An export default declaration is just a short-hand way of exporting an entity with the name default. For example, the module above could instead be written:

const x = {
    name: "hello",
    count: 42
};
export { x as default };

When an export default specifies a single identifier, all meanings of that identifier are exported:

interface Stream { ... }
function Stream(url: string): Stream { ... }
export default Stream;  // Exports a type and a value

An export default declaration can directly declare and export a function or class. The function or class can optionally be named so it can be referenced in the implementing module, but the exported name is always default.

The following exports an unnamed function with the exported name default:

export default function (x: number) {
    return x * x;
}

The following exports a class with the local name Greeter and the exported name default:

export default class Greeter {
    sayHello() {
        console.log("Greetings!");
    }
}

Import Declarations

The exports of a module are imported using import declarations. Import declarations can optionally use as clauses to specify different local names for the imports. For example:

import { read, write, standardOutput as stdout } from "./inout";
var s = read(stdout);
write(stdout, s);

As an alternative to individual imports, a namespace import can be used to import an entire module:

import * as io from "./inout";
var s = io.read(io.standardOutput);
io.write(io.standardOutput, s);

Default Import

The default export of a module is particularly easy to import:

import Greeter from "./greeter";
var g = new Greeter();
g.sayHello();

The above is exactly equivalent to importing the export named default:

import { default as Greeter } from "./greeter";
var g = new Greeter();
g.sayHello();

It is possible to import both the default export and named exports in a single import declaration:

import defaultExport, { namedExport1, namedExport2, namedExport3 } from "./myModule";

Bare Import

A "bare import" can be used to import a module only for its side-effects. Such an import creates no local name bindings.

import "./polyfills";

CommonJS and AMD Code Generation

TypeScript supports down-level compilation of external modules using the new ES6 syntax.

  • When compiling with -t ES3 or -t ES5 a module format must be chosen using -m CommonJS or -m AMD.
  • When compiling with -t ES6 the module format is implicitly assumed to be ECMAScript 6 and the compiler simply emits the original code with type annotations removed.

When compiling down-level for CommonJS or AMD, named exports are emitted as properties on the loader supplied exports instance. This includes default exports which are emitted as assignments to exports.default.

Below are some examples of external modules and the code emitted for CommonJS and AMD.

A module with named exports:

// TypeScript code
function foo() { }
function bar() { }
export { foo, bar as baz };

// Code emitted for CommonJS
function foo() { }
exports.foo = foo;
function bar() { }
exports.baz = bar;

// Code emitted for AMD
define(["require", "exports"], function (require, exports) {
    function foo() { }
    exports.foo = foo;
    function bar() { }
    exports.baz = bar;
});

A module with a default export:

// TypeScript code
export default function foo() { }

// Code emitted for CommonJS
function foo() { }
exports.default = foo;

// Code emitted for AMD
define(["require", "exports"], function (require, exports) {
    function foo() { }
    exports.default = foo;
});

A module with re-exports:

// TypeScript code
export { read, write } from "./inout";
export * from "./utils";

// Code emitted for CommonJS
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
var inout_1 = require("./inout");
exports.read = inout_1.read;
exports.write = inout_1.write;
__export(require("./utils"));

// Code emitted for AMD
define(["require", "exports", "./inout", "./utils"], function (require, exports, inout_1, utils_1) {
    function __export(m) {
        for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
    }
    exports.read = inout_1.read;
    exports.write = inout_1.write;
    __export(utils_1);
});

Importing a module:

// TypeScript code
import { read, write, standardOutput as stdout } from "./inout";
var s = read(stdout);
write(stdout, s);

// Code emitted for CommonJS
var inout_1 = require("./inout");
var s = inout_1.read(inout_1.standardOutput);
inout_1.write(inout_1.standardOutput, s);

// Code emitted for AMD
define(["require", "exports", "./inout"], function (require, exports, inout_1) {
    var s = inout_1.read(inout_1.standardOutput);
    inout_1.write(inout_1.standardOutput, s);
});

Note that destructuring import declarations are rewritten to property accesses on the imported module object. This ensures that exported members can circularly reference each other. For example:

// ------ ping.ts ------
import { pong } from "./pong";
export function ping(count: number) {
    if (count > 0) {
        console.log("ping");
        pong(count - 1);
    }
}

// ------ pong.ts ------
import { ping } from "./ping";
export function pong(count: number) {
    if (count > 0) {
        console.log("pong");
        ping(count - 1);
    }
}

// ------ main.ts ------
import { ping } from "./ping";
ping(10);

This generates the following code when compiled for CommonJS:

// ------ ping.js ------
var pong_1 = require("./pong");
function ping(count) {
    if (count > 0) {
        console.log("ping");
        pong_1.pong(count - 1);
    }
}
exports.ping = ping;

// ------ pong.js ------
var ping_1 = require("./ping");
function pong(count) {
    if (count > 0) {
        console.log("pong");
        ping_1.ping(count - 1);
    }
}
exports.pong = pong;

// ------ main.js ------
var ping_1 = require("./ping");
ping_1.ping(10);

Interoperabitility

An existing external module that doesn't use export = is already ES6 compliant and can be imported using the new ES6 constructs with no additional work.

An external module that uses export = to export another module or a "module like" entity can also 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.

@ahejlsberg ahejlsberg added this to the TypeScript 1.5 milestone Mar 7, 2015

@ahejlsberg ahejlsberg added the Spec label Mar 7, 2015

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 7, 2015

Babel handles mangling default exports with named exports just fine in both AMD and CommonJS. This (amongst other thigs) allows for some nice ways to create default instances of classes, like

export class Logger {
  // stuff
}

export default new Logger(defaultArgs);

which results in the following CommonJS code:

var Logger = exports.Logger = function Logger() {
  _classCallCheck(this, Logger);
};

exports["default"] = new Logger(defaultArgs);
exports.__esModule = true;

When importing this as

import defaultLogger, {Logger} from './log';

it generates the following:

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };

var _log = require("./log");

var defaultLogger = _interopRequire(_log);

var Logger = _log.Logger;

The trick here is the _interopRequire call on default imports, that allows it to work with both es6 modules compiled with the same transpiler, as well as regular AMD/CommonJS modules that use the default export paradigm.

Alxandr commented Mar 7, 2015

Babel handles mangling default exports with named exports just fine in both AMD and CommonJS. This (amongst other thigs) allows for some nice ways to create default instances of classes, like

export class Logger {
  // stuff
}

export default new Logger(defaultArgs);

which results in the following CommonJS code:

var Logger = exports.Logger = function Logger() {
  _classCallCheck(this, Logger);
};

exports["default"] = new Logger(defaultArgs);
exports.__esModule = true;

When importing this as

import defaultLogger, {Logger} from './log';

it generates the following:

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };

var _log = require("./log");

var defaultLogger = _interopRequire(_log);

var Logger = _log.Logger;

The trick here is the _interopRequire call on default imports, that allows it to work with both es6 modules compiled with the same transpiler, as well as regular AMD/CommonJS modules that use the default export paradigm.

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 8, 2015

Oh, and as a side-note, given that typescript typically has metadata about everything, the _interopRequire could be skipped as long as the source is in typescript, or there is a .d.ts file, as it would be known at compile-time the shape of the module in question.

Alxandr commented Mar 8, 2015

Oh, and as a side-note, given that typescript typically has metadata about everything, the _interopRequire could be skipped as long as the source is in typescript, or there is a .d.ts file, as it would be known at compile-time the shape of the module in question.

@NoelAbrahams

This comment has been minimized.

Show comment
Hide comment
@NoelAbrahams

NoelAbrahams Mar 8, 2015

@ahejlsberg,

👍

It is recommended that TypeScript libraries and applications be updated to use the new syntax

I suggest the addendum: "TypeScript's original internal and external module constructs are deprecated and may not be supported in future versions".

(With the aim of discouraging multiple ways of doing the same thing.)

Also I couldn't find anything that says what happens when a module is imported and used in a type-only position. I would expect the down-level emit to omit the require as it does now.

@ahejlsberg,

👍

It is recommended that TypeScript libraries and applications be updated to use the new syntax

I suggest the addendum: "TypeScript's original internal and external module constructs are deprecated and may not be supported in future versions".

(With the aim of discouraging multiple ways of doing the same thing.)

Also I couldn't find anything that says what happens when a module is imported and used in a type-only position. I would expect the down-level emit to omit the require as it does now.

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 9, 2015

Member

@Alxandr It's a nifty scheme, but one issue is that if export default always creates an exports.default property we would have to keep the old export = syntax around for creating modules that want to assign to module.exports and remain consumable by down-level clients that aren't aware of the trick. We would much prefer to retire the old syntax and have everyone move to ES6 syntax.

Member

ahejlsberg commented Mar 9, 2015

@Alxandr It's a nifty scheme, but one issue is that if export default always creates an exports.default property we would have to keep the old export = syntax around for creating modules that want to assign to module.exports and remain consumable by down-level clients that aren't aware of the trick. We would much prefer to retire the old syntax and have everyone move to ES6 syntax.

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 9, 2015

@ahejlsberg babel actually deals with this by special casing files that only export a default to use module.exports. Given that this is a new syntax etc, it would not break compatibility, while still allowing for downstream compatible libraries.

Alxandr commented Mar 9, 2015

@ahejlsberg babel actually deals with this by special casing files that only export a default to use module.exports. Given that this is a new syntax etc, it would not break compatibility, while still allowing for downstream compatible libraries.

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 9, 2015

It's (IMHO at least) better than disallowing default and named exports at the same time.

Alxandr commented Mar 9, 2015

It's (IMHO at least) better than disallowing default and named exports at the same time.

@csnover

This comment has been minimized.

Show comment
Hide comment
@csnover

csnover Mar 10, 2015

Contributor

@ahejlsberg I hear what you are saying about wanting to get everyone on board with the One True Module Format. I have that desire as well. However, I think I also share some of @Alxandr’s concern that if TypeScript isn’t following the rules of that module format more closely that it’s going to cause problems as the “standard” ES6 module format does lots of non-standard things in TypeScript emitting to ES5.

In particular I definitely want people to be to experience the benefits of being able to have circular dependencies on modules with default values, which is currently not possible with the way AMD (and, in some ways, CJS) modules work. This is a fairly important feature when doing things like creating data models that have circular relations to other types, without either using an intermediate registry to retrieve types, or hanging values that should be defaults off of properties (the var Foo = require('Foo').Foo anti-pattern, which TS would have to do, but at least it would be more hidden from developer eyes).

I also understand and share the concern about emitting TS modules for down-level consumers that won’t know this One Weird Trick from Babel to support ES6 modules. I feel like continuing to support export = syntax for this case might be OK since it’s basically an opt-in for the more restrictive default behaviour of legacy module formats. (Of course I don’t do much maintenance of the compiler so YMMV. :))

Please let me know your thoughts on this if you have a moment, I’d like to have some holes poked in my thinking here. Thanks!

Contributor

csnover commented Mar 10, 2015

@ahejlsberg I hear what you are saying about wanting to get everyone on board with the One True Module Format. I have that desire as well. However, I think I also share some of @Alxandr’s concern that if TypeScript isn’t following the rules of that module format more closely that it’s going to cause problems as the “standard” ES6 module format does lots of non-standard things in TypeScript emitting to ES5.

In particular I definitely want people to be to experience the benefits of being able to have circular dependencies on modules with default values, which is currently not possible with the way AMD (and, in some ways, CJS) modules work. This is a fairly important feature when doing things like creating data models that have circular relations to other types, without either using an intermediate registry to retrieve types, or hanging values that should be defaults off of properties (the var Foo = require('Foo').Foo anti-pattern, which TS would have to do, but at least it would be more hidden from developer eyes).

I also understand and share the concern about emitting TS modules for down-level consumers that won’t know this One Weird Trick from Babel to support ES6 modules. I feel like continuing to support export = syntax for this case might be OK since it’s basically an opt-in for the more restrictive default behaviour of legacy module formats. (Of course I don’t do much maintenance of the compiler so YMMV. :))

Please let me know your thoughts on this if you have a moment, I’d like to have some holes poked in my thinking here. Thanks!

@csnover csnover referenced this issue in TypeStrong/atom-typescript Mar 10, 2015

Merged

Typescript 1.5 #179

1 of 1 task complete
@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 10, 2015

Member

@Alxandr I think your suggestion has a lot of merit. Let me summarize what I think we would do.

If a module has only a default export, emit an assignment to module.exports:

// TypeScript code
export default function foo() { }

// Code emitted for CommonJS
function foo() { }
module.exports = foo;

Otherwise, emit everything as assignments to exports.xxx and emit an exports.__esmodule marker:

// TypeScript code
export function foo() { }
export function bar() { }
export default { foo, bar };

// Code emitted for CommonJS
function foo() { }
exports.foo = foo;
function bar() { }
exports.bar = bar;
exports.default = { foo: foo, bar: bar };
exports.__esmodule = true;

On the import side, include an __esmodule check on all default imports:

// TypeScript code
import d, { foo } from "./foobar";
d.foo();
foo();

// Code emitted for CommonJS
var _a = require("./foobar"), d = _a && _a.__esmodule ? _a.default : _a;
d.foo();
_a.foo();

It's not quite as pretty as what is emitted now, but I think it is worth it to get support for full ES6 module semantics down-level (as well as interop with modules emitted by Babel).

For an original import-equals declaration, we would give an error if the imported module has both regular exports and a default export (i.e. if it is an ES6 module). Such modules would only be importable using the new ES6 syntax.

@csnover With this proposal you'd be able to have circular dependencies between modules with default exports as long as the modules have at least one regular export as well (which could just be a dummy member).

Member

ahejlsberg commented Mar 10, 2015

@Alxandr I think your suggestion has a lot of merit. Let me summarize what I think we would do.

If a module has only a default export, emit an assignment to module.exports:

// TypeScript code
export default function foo() { }

// Code emitted for CommonJS
function foo() { }
module.exports = foo;

Otherwise, emit everything as assignments to exports.xxx and emit an exports.__esmodule marker:

// TypeScript code
export function foo() { }
export function bar() { }
export default { foo, bar };

// Code emitted for CommonJS
function foo() { }
exports.foo = foo;
function bar() { }
exports.bar = bar;
exports.default = { foo: foo, bar: bar };
exports.__esmodule = true;

On the import side, include an __esmodule check on all default imports:

// TypeScript code
import d, { foo } from "./foobar";
d.foo();
foo();

// Code emitted for CommonJS
var _a = require("./foobar"), d = _a && _a.__esmodule ? _a.default : _a;
d.foo();
_a.foo();

It's not quite as pretty as what is emitted now, but I think it is worth it to get support for full ES6 module semantics down-level (as well as interop with modules emitted by Babel).

For an original import-equals declaration, we would give an error if the imported module has both regular exports and a default export (i.e. if it is an ES6 module). Such modules would only be importable using the new ES6 syntax.

@csnover With this proposal you'd be able to have circular dependencies between modules with default exports as long as the modules have at least one regular export as well (which could just be a dummy member).

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 10, 2015

@ahejlsberg wouldn't it be possible to skip the fancy emit given metadata? I mean, typescript has typeinformation about everything (which is sort of the idea, right)? So if we know that the module being imported, we should know the format it exports at, right?

Alxandr commented Mar 10, 2015

@ahejlsberg wouldn't it be possible to skip the fancy emit given metadata? I mean, typescript has typeinformation about everything (which is sort of the idea, right)? So if we know that the module being imported, we should know the format it exports at, right?

@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 10, 2015

Contributor

Is it better to give an error for using import-equals to import an es6 module? Or is it better to emit an import-equals declaration in the same way as a default import? We could emit:

import d = require("./foobar");

as

var _a = require("./foobar"), d = _a && _a.__esModule ? _a.default : _a;
Contributor

JsonFreeman commented Mar 10, 2015

Is it better to give an error for using import-equals to import an es6 module? Or is it better to emit an import-equals declaration in the same way as a default import? We could emit:

import d = require("./foobar");

as

var _a = require("./foobar"), d = _a && _a.__esModule ? _a.default : _a;
@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 10, 2015

Contributor

Also, to make circular references work, don't you have to access the default member late? So instead of assigning the default to d eagerly, a call to d.foo() would emit as _a.default.foo(). Why is this not the case, but for named exports it is?

Contributor

JsonFreeman commented Mar 10, 2015

Also, to make circular references work, don't you have to access the default member late? So instead of assigning the default to d eagerly, a call to d.foo() would emit as _a.default.foo(). Why is this not the case, but for named exports it is?

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 10, 2015

Member

@Alxandr Yes, I think it would work to have the following rules:

  • On export, emit a module.exports assignment when a module exports only a default, and emit everything as assignments to exports.xxx otherwise.
  • On import, assume require returns the default export object itself when importing a module that exports only a default, and assume everything is a property on the returned object otherwise.

We would lose the ability to dynamically adapt on import based on the __esModule marker, but that would be ok as long as everyone else plays by the same rules.

I suppose we'd still want to emit the __esModule marker such that Babel and other systems not guided by static type information can do the right thing.

@JsonFreeman I think we have two choices for import-equals with an ES6 (mixed) module. Either say it is an error (there's no backwards compatibility to worry about) or say that you get the module object with a set of properties including one named default. The odd thing about the latter is that adding a regular export to a module that previously had only a default export would cause everything to "pop out" one level on the import-equals side. My personal inclination is to make import-equals an error with mixed modules.

Regarding circular references, you're right, we'd want to rewrite references to the default import in the same way we'd do with any other import. Which in turn means we don't want the dynamic _esModule import check. One more reason not to do it.

Member

ahejlsberg commented Mar 10, 2015

@Alxandr Yes, I think it would work to have the following rules:

  • On export, emit a module.exports assignment when a module exports only a default, and emit everything as assignments to exports.xxx otherwise.
  • On import, assume require returns the default export object itself when importing a module that exports only a default, and assume everything is a property on the returned object otherwise.

We would lose the ability to dynamically adapt on import based on the __esModule marker, but that would be ok as long as everyone else plays by the same rules.

I suppose we'd still want to emit the __esModule marker such that Babel and other systems not guided by static type information can do the right thing.

@JsonFreeman I think we have two choices for import-equals with an ES6 (mixed) module. Either say it is an error (there's no backwards compatibility to worry about) or say that you get the module object with a set of properties including one named default. The odd thing about the latter is that adding a regular export to a module that previously had only a default export would cause everything to "pop out" one level on the import-equals side. My personal inclination is to make import-equals an error with mixed modules.

Regarding circular references, you're right, we'd want to rewrite references to the default import in the same way we'd do with any other import. Which in turn means we don't want the dynamic _esModule import check. One more reason not to do it.

@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 10, 2015

Contributor

In terms of backward compatibility, importing code that previously did not error, would now error if the exporting module suddenly starts exporting other stuff besides its default export. But I guess the argument is, in that case it's better to get an error than to suddenly get different semantics. So I guess in that sense, there is no real break of backward compatibility.

@ahejlsberg, you mentioned skipping the dynamic check on the import side. I agree it's nicer to not have it, but I have one question. Does this mean that the following assigns directly to module.exports?

class C { }
export { C as default };
Contributor

JsonFreeman commented Mar 10, 2015

In terms of backward compatibility, importing code that previously did not error, would now error if the exporting module suddenly starts exporting other stuff besides its default export. But I guess the argument is, in that case it's better to get an error than to suddenly get different semantics. So I guess in that sense, there is no real break of backward compatibility.

@ahejlsberg, you mentioned skipping the dynamic check on the import side. I agree it's nicer to not have it, but I have one question. Does this mean that the following assigns directly to module.exports?

class C { }
export { C as default };
@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 10, 2015

Contributor

What about this? Would this assign directly to module.exports?:

// In a file A.ts
export default class { };

// In a file B.ts
export * from "A"; // Does this assign directly to module.exports? Or just an empty namespace?
Contributor

JsonFreeman commented Mar 10, 2015

What about this? Would this assign directly to module.exports?:

// In a file A.ts
export default class { };

// In a file B.ts
export * from "A"; // Does this assign directly to module.exports? Or just an empty namespace?
@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 10, 2015

Member

@JsonFreeman Yes, your class first example would assign directly to module.exports. Writing

export { C as default };

is precisely equivalent to writing

export default C;

Regarding your second example, an export * never re-exports default exports, so it would never assign to module.exports.

Member

ahejlsberg commented Mar 10, 2015

@JsonFreeman Yes, your class first example would assign directly to module.exports. Writing

export { C as default };

is precisely equivalent to writing

export default C;

Regarding your second example, an export * never re-exports default exports, so it would never assign to module.exports.

@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 10, 2015

Contributor

Great, thanks. I believe this design is consistent and reasonable.

Contributor

JsonFreeman commented Mar 10, 2015

Great, thanks. I believe this design is consistent and reasonable.

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 12, 2015

Member

Yes, the spec specifically allows the identifier following as in an export clause to be a reserved word.

https://people.mozilla.org/~jorendorff/es6-draft.html#sec-exports

Member

ahejlsberg commented Mar 12, 2015

Yes, the spec specifically allows the identifier following as in an export clause to be a reserved word.

https://people.mozilla.org/~jorendorff/es6-draft.html#sec-exports

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 12, 2015

@jbondc I don't think the first export there is legal. At least babel throws on the ..

Alxandr commented Mar 12, 2015

@jbondc I don't think the first export there is legal. At least babel throws on the ..

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 12, 2015

@jbondc From using ES6 with babel for a good while, I've almost never used export {..}. In general you just export values as you create them.

Another point that pooped up from your question though is exports inside of internal modules. How will that be handled? Do I do the following?

export module Foo {
  export class Bar {}
}

or is the following enough

module Foo {
  export class Bar {}
}

And how do I import it?

import { Foo } from './file';
new Foo.Bar();

Or will internal modules get removed down the line, as they were from the module draft for ES6?

Alxandr commented Mar 12, 2015

@jbondc From using ES6 with babel for a good while, I've almost never used export {..}. In general you just export values as you create them.

Another point that pooped up from your question though is exports inside of internal modules. How will that be handled? Do I do the following?

export module Foo {
  export class Bar {}
}

or is the following enough

module Foo {
  export class Bar {}
}

And how do I import it?

import { Foo } from './file';
new Foo.Bar();

Or will internal modules get removed down the line, as they were from the module draft for ES6?

@jbondc

This comment has been minimized.

Show comment
Hide comment
@jbondc

jbondc Mar 13, 2015

Contributor

@Alxandr The TypeScript team is considering renaming 'module' to 'namespace' #2159
You'd have to 'export module' or 'export namespace' for it to be importable.

Contributor

jbondc commented Mar 13, 2015

@Alxandr The TypeScript team is considering renaming 'module' to 'namespace' #2159
You'd have to 'export module' or 'export namespace' for it to be importable.

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 13, 2015

Member

@Alxandr @jbondc A TypeScript internal module is really no different than other declarable entities such as classes, functions, and enums when it comes external modules. For example, given this external module that exports an internal module

export module Foo {
    export class Bar { }
}

you can import as follows

import { Foo } from "./mod";
new Foo.Bar();

However, as you've observed, ES6 import and export declarations don't allow you to "dot into" the substructure of internal modules (understandable, as they aren't part of ES6). So, for example, the following is not allowed:

import { Foo.Bar as Bar } from "./mod";  // Error, qualified name not allowed

You would have to do it in two steps by adding a TypeScript import-equals:

import { Foo } from "./mod";
import Bar = Foo.Bar;

In general I don't think it will be common to mix the two, nor is it clear that we want to encourage it.

Member

ahejlsberg commented Mar 13, 2015

@Alxandr @jbondc A TypeScript internal module is really no different than other declarable entities such as classes, functions, and enums when it comes external modules. For example, given this external module that exports an internal module

export module Foo {
    export class Bar { }
}

you can import as follows

import { Foo } from "./mod";
new Foo.Bar();

However, as you've observed, ES6 import and export declarations don't allow you to "dot into" the substructure of internal modules (understandable, as they aren't part of ES6). So, for example, the following is not allowed:

import { Foo.Bar as Bar } from "./mod";  // Error, qualified name not allowed

You would have to do it in two steps by adding a TypeScript import-equals:

import { Foo } from "./mod";
import Bar = Foo.Bar;

In general I don't think it will be common to mix the two, nor is it clear that we want to encourage it.

@rotemdan

This comment has been minimized.

Show comment
Hide comment
@rotemdan

rotemdan Mar 15, 2015

Having spent a total of several hours trying to come up with a new ES6 style syntax to apply typeof on an ambient external module (#2357), and closely evaluating the ES6 import syntax, there is one thing I found truly counter-intuitive, almost to the point of doubting my own understanding of it:

The from keyword intuitively seems to imply that something is "chosen" from the module, yet, ES6 designers use it in quite a bizarre way, for example:

import {FileReader} from "FileSystem";

as expected, would import the export (let's say in this case a class) FileReader from the module, yet

import FileReader from "FileSystem";

would unexpectedly import the default export from the module and assign it the identifier FileReader.

Apart from being a huge pitfall for human error and confusion, I also think it "abuses" the from keyword in an unappealing way. As I see it: import.. from means "choose something from", not "apply alias to default export". A more meaningful syntax would have been:

import "FileSystem" as FS;

where the as keyword is interpreted with the natural semantics most people would assign to it.

Having spent a total of several hours trying to come up with a new ES6 style syntax to apply typeof on an ambient external module (#2357), and closely evaluating the ES6 import syntax, there is one thing I found truly counter-intuitive, almost to the point of doubting my own understanding of it:

The from keyword intuitively seems to imply that something is "chosen" from the module, yet, ES6 designers use it in quite a bizarre way, for example:

import {FileReader} from "FileSystem";

as expected, would import the export (let's say in this case a class) FileReader from the module, yet

import FileReader from "FileSystem";

would unexpectedly import the default export from the module and assign it the identifier FileReader.

Apart from being a huge pitfall for human error and confusion, I also think it "abuses" the from keyword in an unappealing way. As I see it: import.. from means "choose something from", not "apply alias to default export". A more meaningful syntax would have been:

import "FileSystem" as FS;

where the as keyword is interpreted with the natural semantics most people would assign to it.

@jbondc

This comment has been minimized.

Show comment
Hide comment
@jbondc

jbondc Mar 15, 2015

Contributor

@rotemdan It's popped up on es and possibly other places:
https://esdiscuss.org/topic/import-default-syntax

Another part that's been mentioned is:
a) export default Ts.Is.Cool.bar
vs.
b) export default = Ts.Is.Cool.bar

There seemed to be a preference for (b)
Good read but unclear how "final" the syntax is:
http://www.2ality.com/2014/09/es6-modules-final.html

Contributor

jbondc commented Mar 15, 2015

@rotemdan It's popped up on es and possibly other places:
https://esdiscuss.org/topic/import-default-syntax

Another part that's been mentioned is:
a) export default Ts.Is.Cool.bar
vs.
b) export default = Ts.Is.Cool.bar

There seemed to be a preference for (b)
Good read but unclear how "final" the syntax is:
http://www.2ality.com/2014/09/es6-modules-final.html

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 15, 2015

Member

@Alxandr @JsonFreeman Having given some more thought to whether metadata should guide the code generation for ES6 import declarations, I now think that it shouldn't.

The problem with the metadata guided approach is that it only works when modules are compiled together. For example, say that module "a" is a default export only module and that "b" imports the default export of "a". Now say that "a" adds a regular export, thus becoming a mixed module. "b" now needs to be recompiled because the code to import the default export of "a" is different. For "b" to be unaffected by such changes in "a" we need to include dynamic __esModule checks in imports of default exports.

Also, only by including __esModule checks is it possible to emit code for a given module without resolving its dependencies, and we definitely want that for features such as compile-on-save.

@csnover The upshot of this is that imports of default exports will always be evaluated eagerly (as they are now), and I don't see any way in which we could make circular references between default exports work in CommonJS or AMD.

Member

ahejlsberg commented Mar 15, 2015

@Alxandr @JsonFreeman Having given some more thought to whether metadata should guide the code generation for ES6 import declarations, I now think that it shouldn't.

The problem with the metadata guided approach is that it only works when modules are compiled together. For example, say that module "a" is a default export only module and that "b" imports the default export of "a". Now say that "a" adds a regular export, thus becoming a mixed module. "b" now needs to be recompiled because the code to import the default export of "a" is different. For "b" to be unaffected by such changes in "a" we need to include dynamic __esModule checks in imports of default exports.

Also, only by including __esModule checks is it possible to emit code for a given module without resolving its dependencies, and we definitely want that for features such as compile-on-save.

@csnover The upshot of this is that imports of default exports will always be evaluated eagerly (as they are now), and I don't see any way in which we could make circular references between default exports work in CommonJS or AMD.

@Alxandr

This comment has been minimized.

Show comment
Hide comment
@Alxandr

Alxandr Mar 15, 2015

@ahejlsberg Ah, yeah, I completely agree with this.

Alxandr commented Mar 15, 2015

@ahejlsberg Ah, yeah, I completely agree with this.

@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 16, 2015

Contributor

I think that is reasonable, given that we want isolated, single file emit as a goal. If this is important, we will also need to visit other places in the language to make sure that the whole language is single-file-emit safe.

Contributor

JsonFreeman commented Mar 16, 2015

I think that is reasonable, given that we want isolated, single file emit as a goal. If this is important, we will also need to visit other places in the language to make sure that the whole language is single-file-emit safe.

@csnover

This comment has been minimized.

Show comment
Hide comment
@csnover

csnover Mar 16, 2015

Contributor

@csnover The upshot of this is that imports of default exports will always be evaluated eagerly (as they are now), and I don't see any way in which we could make circular references between default exports work in CommonJS or AMD.

If you are saying that using __esModule runtime flag is the solution, doing the check at the usage site and not at the import reduces efficiency and adds a little ugliness but would ensure that the circular dependency handling works, I think:

define([ 'exports', 'a' ], function (exports, a) {
  function b() {
    (a && a.__esModule ? a.default : a)();
  }
  exports.default = b;
});

or you could use a function to focus the ugly check to one place but is slower:

define([ 'exports', 'a' ], function (exports, a) {
  function __default(obj) { return obj.__esModule ? obj['default'] : obj; }

  function b() {
    __default(a)();
  }
  exports.default = b;
});

The only reason I push on this is because the default import/export sugar is one of the only real benefits of the ES module format, so it would be a shame to be deprived of its use. Even an opt-in flag like Esperanto’s strict mode flag would be fine, so people that want to write ES modules with ES semantics can get that, and people that want to write ES modules with legacy semantics can avoid the overhead.

Contributor

csnover commented Mar 16, 2015

@csnover The upshot of this is that imports of default exports will always be evaluated eagerly (as they are now), and I don't see any way in which we could make circular references between default exports work in CommonJS or AMD.

If you are saying that using __esModule runtime flag is the solution, doing the check at the usage site and not at the import reduces efficiency and adds a little ugliness but would ensure that the circular dependency handling works, I think:

define([ 'exports', 'a' ], function (exports, a) {
  function b() {
    (a && a.__esModule ? a.default : a)();
  }
  exports.default = b;
});

or you could use a function to focus the ugly check to one place but is slower:

define([ 'exports', 'a' ], function (exports, a) {
  function __default(obj) { return obj.__esModule ? obj['default'] : obj; }

  function b() {
    __default(a)();
  }
  exports.default = b;
});

The only reason I push on this is because the default import/export sugar is one of the only real benefits of the ES module format, so it would be a shame to be deprived of its use. Even an opt-in flag like Esperanto’s strict mode flag would be fine, so people that want to write ES modules with ES semantics can get that, and people that want to write ES modules with legacy semantics can avoid the overhead.

@mhegazy mhegazy added the ES6 label Mar 17, 2015

@ahejlsberg

This comment has been minimized.

Show comment
Hide comment
@ahejlsberg

ahejlsberg Mar 19, 2015

Member

@mhegazy and I spent some time over the past few days thinking about the feedback we've gotten here and elsewhere. We've come to the consensus that export default should consistently have ES module semantics and not be conflated with export =. Specifically:

  • 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.

This approach has several advantages:

  • It reduces the amount of magic used in mapping ES6 modules onto CommonJS/AMD.
  • It gives us a better story for interop with existing declaration files.
  • It allows circularly dependent modules to work in a consistent manner (including circularly dependent default exports). As has been pointed out here, the ability to have circular module references is actually one of the major advantages of the ES6 module design and it is important for us to preserve this attribute in down-level scenarios (best I can tell, Babel doesn't do so).

An example of circularly dependent ES6 modules:

// ------ ping.ts ------
import pong from "./pong";
export default function (count: number) {
    if (count > 0) {
        console.log("ping");
        pong(count - 1);
    }
}

// ------ pong.ts ------
import ping from "./ping";
export default function (count: number) {
    if (count > 0) {
        console.log("pong");
        ping(count - 1);
    }
}

// ------ main.ts ------
import ping from "./ping";
ping(10);

The emitted code for CommonJS:

// ------ ping.js ------
var pong = require("./pong");
function _default(count) {
    if (count > 0) {
        console.log("ping");
        pong.default(count - 1);
    }
}
exports.default = _default;

// ------ pong.js ------
var ping = require("./ping");
function _default(count) {
    if (count > 0) {
        console.log("pong");
        ping.default(count - 1);
    }
}
exports.default = _default;

// ------ main.js ------
var ping = require("./ping");
ping.default(10);

An example of interop with existing .d.ts files:

// ------ angular.d.ts ------
declare module angular {
    export function bind(context: any, fn: Function, ...args: any[]): Function;
    export function bootstrap(element: string, modules?: string, config?: any): any;
    // more
}
declare module "angular" {
    export = angular;
}

// ------ client.ts ------
/// <reference path="angular.d.ts"/>
import { bind, bootstrap } from "angular";
bind;
bootstrap;

The emitted code for AMD:

// ------ client.js ------
define(["require", "exports", "angular"], function (require, exports, _angular) {
    _angular.bind;
    _angular.bootstrap;
});
Member

ahejlsberg commented Mar 19, 2015

@mhegazy and I spent some time over the past few days thinking about the feedback we've gotten here and elsewhere. We've come to the consensus that export default should consistently have ES module semantics and not be conflated with export =. Specifically:

  • 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.

This approach has several advantages:

  • It reduces the amount of magic used in mapping ES6 modules onto CommonJS/AMD.
  • It gives us a better story for interop with existing declaration files.
  • It allows circularly dependent modules to work in a consistent manner (including circularly dependent default exports). As has been pointed out here, the ability to have circular module references is actually one of the major advantages of the ES6 module design and it is important for us to preserve this attribute in down-level scenarios (best I can tell, Babel doesn't do so).

An example of circularly dependent ES6 modules:

// ------ ping.ts ------
import pong from "./pong";
export default function (count: number) {
    if (count > 0) {
        console.log("ping");
        pong(count - 1);
    }
}

// ------ pong.ts ------
import ping from "./ping";
export default function (count: number) {
    if (count > 0) {
        console.log("pong");
        ping(count - 1);
    }
}

// ------ main.ts ------
import ping from "./ping";
ping(10);

The emitted code for CommonJS:

// ------ ping.js ------
var pong = require("./pong");
function _default(count) {
    if (count > 0) {
        console.log("ping");
        pong.default(count - 1);
    }
}
exports.default = _default;

// ------ pong.js ------
var ping = require("./ping");
function _default(count) {
    if (count > 0) {
        console.log("pong");
        ping.default(count - 1);
    }
}
exports.default = _default;

// ------ main.js ------
var ping = require("./ping");
ping.default(10);

An example of interop with existing .d.ts files:

// ------ angular.d.ts ------
declare module angular {
    export function bind(context: any, fn: Function, ...args: any[]): Function;
    export function bootstrap(element: string, modules?: string, config?: any): any;
    // more
}
declare module "angular" {
    export = angular;
}

// ------ client.ts ------
/// <reference path="angular.d.ts"/>
import { bind, bootstrap } from "angular";
bind;
bootstrap;

The emitted code for AMD:

// ------ client.js ------
define(["require", "exports", "angular"], function (require, exports, _angular) {
    _angular.bind;
    _angular.bootstrap;
});
@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Mar 19, 2015

Contributor

So now everything has a distinct meaning, nothing is synonymous with anything, correct?

Contributor

JsonFreeman commented Mar 19, 2015

So now everything has a distinct meaning, nothing is synonymous with anything, correct?

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Mar 19, 2015

Contributor

So now everything has a distinct meaning

I think var foo; export foo etc. is still synonymous with external modules.

Contributor

basarat commented Mar 19, 2015

So now everything has a distinct meaning

I think var foo; export foo etc. is still synonymous with external modules.

hoqqanen pushed a commit to Asana/DefinitelyTyped that referenced this issue Dec 17, 2015

George Hoqqanen
Fix react addons update export style.
This was causing a typescript compilation error, "Module '"react-addons-update"' resolves to a non-module entity and cannot be imported using this construct."
See Microsoft/TypeScript#2242,
"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."

@novemberborn novemberborn referenced this issue in avajs/ava Feb 26, 2016

Closed

Add TypeScript typings #571

@eugenpodaru eugenpodaru referenced this issue in DefinitelyTyped/DefinitelyTyped Mar 25, 2016

Merged

Support AMD require / ES6 import for text-encoding #8721

@schmuli schmuli referenced this issue in NorthwoodsSoftware/GoJS Apr 13, 2016

Closed

Update to Support TypeScript Module Resolution #17

@texastoland

This comment has been minimized.

Show comment
Hide comment
@texastoland

texastoland Apr 23, 2016

Contributor

I believe this got implemented in 1.8 by --allowSyntheticDefaultImports in #5577.

Contributor

texastoland commented Apr 23, 2016

I believe this got implemented in 1.8 by --allowSyntheticDefaultImports in #5577.

@toddlucas toddlucas referenced this issue in DefinitelyTyped/DefinitelyTyped May 3, 2016

Merged

Fix isomorphic-fetch module setup #9144

@JKillian JKillian referenced this issue in palantir/tslint May 4, 2016

Merged

adding no-default-export rule #1209

cwalv referenced this issue in cwalv/DefinitelyTyped May 10, 2016

@ngbrown ngbrown referenced this issue in DefinitelyTyped/DefinitelyTyped May 26, 2016

Merged

react-tap-event-plugin: include shouldRejectClick option. #9444

@SamVerschueren SamVerschueren referenced this issue in benlesh/symbol-observable May 27, 2016

Closed

fix(types): use default syntax for typedef #11

@miguelcobain

This comment has been minimized.

Show comment
Hide comment
@miguelcobain

miguelcobain Jun 1, 2016

Sorry for bringing this up.
After reading this, I looks to me that it is impossible to to write a typings file that is compatible with both these import syntaxes:

import express from 'express';

and

import express = require('express');

Is this correct or did I get the wrong idea?
Most typings file out there use export = something instead of named exports, which I think is really unfortunate.

To be more concrete, when I use this typings file: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/express/express.d.ts

I'd like to be able to write import express, { Router } from 'express'; and still be compatible with people that write import express = require('express');. What changes need to done to the typings file.
Unfortunately most typings file don't have named exports which looks like a step backwards to me.

Thanks.

Sorry for bringing this up.
After reading this, I looks to me that it is impossible to to write a typings file that is compatible with both these import syntaxes:

import express from 'express';

and

import express = require('express');

Is this correct or did I get the wrong idea?
Most typings file out there use export = something instead of named exports, which I think is really unfortunate.

To be more concrete, when I use this typings file: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/express/express.d.ts

I'd like to be able to write import express, { Router } from 'express'; and still be compatible with people that write import express = require('express');. What changes need to done to the typings file.
Unfortunately most typings file don't have named exports which looks like a step backwards to me.

Thanks.

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Jun 2, 2016

Member

@miguelcobain it sounds like what you actually want to do is compile with --allowSyntheticDefaultImports ? Or does express actually have a member named default that points to the containing object?

Member

RyanCavanaugh commented Jun 2, 2016

@miguelcobain it sounds like what you actually want to do is compile with --allowSyntheticDefaultImports ? Or does express actually have a member named default that points to the containing object?

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Jun 2, 2016

Contributor

Or does express actually have a member named default that points to the containing object

oh oh oh 🙋 I know the answer : no 🙅 🌹

Contributor

basarat commented Jun 2, 2016

Or does express actually have a member named default that points to the containing object

oh oh oh 🙋 I know the answer : no 🙅 🌹

@miguelcobain

This comment has been minimized.

Show comment
Hide comment
@miguelcobain

miguelcobain Jun 3, 2016

@RyanCavanaugh that option was what I was looking for. Many thanks!

@RyanCavanaugh that option was what I was looking for. Many thanks!

afc163 referenced this issue in ant-design/ant-design Jul 12, 2016

@afc163 afc163 referenced this issue in ant-design/ant-design Jul 12, 2016

Closed

rewrite in typescript #1846

47 of 47 tasks complete
@tikhonandrey

This comment has been minimized.

Show comment
Hide comment
@tikhonandrey

tikhonandrey Aug 25, 2016

Hi!
I can suggest a compromise solution for projects with webpack.
es6 module default export will be fixed and bundle will be faster because babel use only one plugin
import React from 'react';
and u doesn't need to change it to:
import * as React from 'react';
try this
//file webpack.config.js

...
loaders: [{
                test: /\.tsx?$/,
                loaders: ['babel','ts'],
                exclude: /(bower_components|node_modules|typings)/,
 }],
...

//file tsconfig.json

{
    "version": "1.8.0",
    "compilerOptions": {
        "target": "es6",
        "sourceMap": true,
        "jsx": "react",
        "experimentalDecorators": true
    },
    "files":[
        "app/scripts/lib.d.ts"
    ]
}
//file .babelrc
{
     "plugins": ["transform-es2015-modules-commonjs"]
}

links:
ES6 modules with TypeScript and webpack
BABEL ES2015 modules to CommonJS transform

Hi!
I can suggest a compromise solution for projects with webpack.
es6 module default export will be fixed and bundle will be faster because babel use only one plugin
import React from 'react';
and u doesn't need to change it to:
import * as React from 'react';
try this
//file webpack.config.js

...
loaders: [{
                test: /\.tsx?$/,
                loaders: ['babel','ts'],
                exclude: /(bower_components|node_modules|typings)/,
 }],
...

//file tsconfig.json

{
    "version": "1.8.0",
    "compilerOptions": {
        "target": "es6",
        "sourceMap": true,
        "jsx": "react",
        "experimentalDecorators": true
    },
    "files":[
        "app/scripts/lib.d.ts"
    ]
}
//file .babelrc
{
     "plugins": ["transform-es2015-modules-commonjs"]
}

links:
ES6 modules with TypeScript and webpack
BABEL ES2015 modules to CommonJS transform

@HerringtonDarkholme HerringtonDarkholme referenced this issue in vuejs/vue-loader Sep 13, 2016

Merged

add esModule support #349

@teyc

This comment has been minimized.

Show comment
Hide comment
@teyc

teyc Sep 18, 2016

Sorry - alm issue pls ignore

I'm running node v6.1.0 and I'm getting the following error

MOCHA STDERR: > /usr/local/lib/node_modules/alm/src/server/workers/tested/runners/mochaInstrumenter.ts:9 import * as common from "./instrumenterCommon";

teyc commented Sep 18, 2016

Sorry - alm issue pls ignore

I'm running node v6.1.0 and I'm getting the following error

MOCHA STDERR: > /usr/local/lib/node_modules/alm/src/server/workers/tested/runners/mochaInstrumenter.ts:9 import * as common from "./instrumenterCommon";

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Sep 18, 2016

Contributor

@teyc That looks like an alm error. Feel free to create an issue there https://github.com/alm-tools/alm/issues but you will have to provide more information / reproduction steps 🌹

Contributor

basarat commented Sep 18, 2016

@teyc That looks like an alm error. Feel free to create an issue there https://github.com/alm-tools/alm/issues but you will have to provide more information / reproduction steps 🌹

@DataTables DataTables referenced this issue in palantir/tslint Sep 20, 2016

Closed

no-reference with type definition files #1562

@Stubb0rn Stubb0rn referenced this issue in DefinitelyTyped/DefinitelyTyped Sep 24, 2016

Merged

Added type definition for eventemitter3-1.2.0 #10857

3 of 3 tasks complete

@iamolivinius iamolivinius referenced this issue in localForage/localForage Oct 11, 2016

Merged

docs(README): update Typescript notice for v2.0 #608

@qvazzler qvazzler referenced this issue in andrewplummer/Sugar Nov 23, 2016

Open

TypeScript type definitions #529

@tobich tobich referenced this issue in wix-incubator/tspoon Jan 1, 2017

Open

Additional hooks into the Transpile process #121

@albohlabs albohlabs referenced this issue in svgdotjs/svg.js Jan 12, 2017

Merged

Add UMD module support Add Matrix property #565

@barake barake referenced this issue in Profiscience/ko-component-router Feb 22, 2017

Closed

Typescript type declarations #142

@trotyl trotyl referenced this issue in NG-ZORRO/ng-zorro-antd Aug 18, 2017

Closed

Not support for Rollup in AOT mode #85

@lajosbencz lajosbencz referenced this issue in lajosbencz/vue-wamp Sep 26, 2017

Open

Use vue-wamp with typescript #9

@BehindTheMath BehindTheMath referenced this issue in DefinitelyTyped/DefinitelyTyped Oct 19, 2017

Merged

[github-username-regex] Add definitions #20665

9 of 9 tasks complete

@danmarshall danmarshall referenced this issue in Pomax/bezierjs Oct 31, 2017

Open

Typescript support #65

@jamesssooi jamesssooi referenced this issue in jamesssooi/Croppr.js Apr 12, 2018

Open

No Default Export in Typescript #15

@snowyu snowyu referenced this issue in pouchdb-community/pouchdb-authentication May 14, 2018

Closed

* [bug] no default import on ts-es6 #237

@Microsoft Microsoft locked and limited conversation to collaborators Jun 18, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.