Closed DanielRosenwasser closed 6 years ago
Although synthetic defaults still seem to work side-by-side with namespace interpretations. It would have to be a form of synthetic that disallowed non-default named exports entirely.
@guybedford for my own reference for such a future flag, I should note that import d from "cjs";
, import {default as d} from "cjs";
, and import * as o from "cjs"; const d = o.default;
should all be valid (and identical) in such a scheme, so it's not just some simple syntactic ban on no default forms.
Sure thanks @weswigham for noting it. It's good to prepare for this to dampen the landing as modules hit platform reality.
TL;DR
Flags like--allowSyntheticDefaultImports
will just work without extra tools like Webpack, Babel, or SystemJS.TypeScript will just work with the
--ESModuleInterop
flag without extra tools like Webpack, Babel, or SystemJS.See the PR at #19675 for more details.
Background
TypeScript has successfully delivered ES modules for quite some time now. Unfortunately, the implementation of ES/CommonJS interop that Babel and TypeScript delivered differs in ways that make migration difficult between the two.
TypeScript treats a namespace import (i.e.
import * as foo from "foo"
) as equivalent toconst foo = require("foo")
. Things are simple here, but they don't work out if the primary object being imported is a primitive or a value with call/construct signatures. ECMAScript basically says a namespace record is a plain object.Babel first requires in the module, and checks for a property named
__esModule
. If__esModule
is set totrue
, then the behavior is the same as that of TypeScript, but otherwise, it synthesizes a namespace record where:require
'd module and made available as named imports.require
'd module is made available as adefault
import.This looks something like the following transform:
As an optimization, the following are performed:
If only named properties are ever used on an import, *the
require
'd object is used in place of creating a namespace record.Note that this is already TypeScript's behavior!
If
__esModule
flag is not set on therequire
'd value, thenA fresh namespace record whose
default
export refers to therequire
'd value will be created in place of therequire
'd value. In other words:Note that there is no optimization for never using the default. This is probably because you'd need alias analysis and could never be sure that someone else would use the
default
.Proposal
I believe that we'd serve both communities well if we decided to adopt the aforementioned emit.
Drawbacks
Performance & Size Impact
This certainly impacts the size and readability of TypeScript's output. Furthermore, for large CommonJS modules, there could be a speed impact to keep in mind - notice that these synthesized namespace records are not cached at all.
Tools already do this for us
Those who've been using tools like Webpack 2 and SystemJS have been getting this functionality for a few months now. For example, if you target ES modules, Webpack 2 will already perform this behavior. Taking this strategy makes it an opt-in for users who care about the interop behavior.
I mainly bring this up because I want to immediately bring up the counterpoint. While these tools are definitely central to many modern web stacks, they won't cover