Default export behavior when module "default" key is undefined
soyuka opened this issue · 3 comments
TypeScript Version: 2.1.4 / tried with current nightly yesterday
Linked issues :
rollup/rollup#1156
infernojs/inferno#596
Code
Inferno exports something like:
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('./inferno')) :
typeof define === 'function' && define.amd ? define(['exports', 'inferno'], factory) :(factory((global.Inferno = global.Inferno || {}),global.inferno));
In typescript, it is export default class Component{}
.
When imported, you'll expect this to work:
import createElement from 'inferno-create-element'
import Component from 'inferno-component'
console.log(createElement)
console.log(Component)
But those will both be undefined because the typescript compiler exports something like:
console.log(inferno_create_element_1.default);
console.log(inferno_component_1.default);
I also tried to import as:
import * as createElement from 'inferno-create-element'
import * as Component from 'inferno-component'
Which does bring back the correct values, except their signature is now wrong and you can not use them as intended (for example class extend Component
).
Reproduction
Here is a reproduction repository:
https://github.com/soyuka/repro-default-export
Clone and run bash init.sh
.
Note that:
- Inferno creator says typescript fixed this issue (I tried latest + next without success)
- Rollup (which is used by inferno for umd bundle) says it might be a typescript issue
- There is no difference with or without webpack, therefore it's definitely not a webpack issue
To me it's probably that typescript always expect a default key, which is why I made a proposal to add the following to rollup:
//add this and everything works:
if (typeof module !== 'undefined') {
module.exports.default = module.exports
}
Would you be able to give me some details about the typescript expected behavior? Is this a rollup issue or a typescript one?
Thanks!
The ES6 module semantics are very clear on these points:
import x from 'y'
gets thedefault
export from'y'
import * as q from 'z'
cannot possibly produce aq
which can be legally invoked withnew
It'd be incorrect for us to emit anything other than .default
for the first situation and it's incorrect for us to allow you to try to new q
or q()
in the second situation.
If the top-level export of the inferno-create-element
module is really a class or function, then it is not a compliant ES6 module and should not be imported with ES6 syntax. See http://stackoverflow.com/questions/39415661/why-cant-i-import-a-class-or-function-with-import-as-x-from-y . In this case, use import x = require('y');
syntax because this is a CommonJS-style module.
It's possible but unlikely that the allowSyntheticDefaultImports
flag might help you. Worth mentioning just in case.
Thank you very much for this comprehensive answer, I'll check this out and report back. In the time being, I'm closing this.
Leaving this here if somebody is gonna stumble on this thread again when having troubles with setting up his/her build pipeline relaying on rollup.
From my point of view if you want to provide a structure that is consumable for your TS typings, you should simply use exports: 'named'
in your projects (in rollup.config.js
when bundling), instead of relaying on automatic behaviour.
Going further - if you do not enforcing CJS consumers to use require('your-lib').default
instead of require('your-lib')
you should just use additionally this babel plugin - https://github.com/59naga/babel-plugin-add-module-exports (only when transpiling to CJS, do not use it when transpiling to ESM format)