-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
esModuleInterop does not handle named imports when combined with synthetic default imports #21621
Comments
This is working as intended, then. CommonJS modules only have a Code that did |
@Kovensky This is not intended. We should be emitting the Specifically:
This is still under discussion is by no means set yet (we're watching it carefully). While it will be available as the default (which is what makes the flag needed), most transpilers today also allow named accesses of commonjs modules; and the goal of this flag is mostly to align with other transpilers and give people a path to migrate forward (since TS'd usual behavior wouldn't allow that) @frankwallis as a workaround, if you split up the import into default/not default import statements it should work until we issue a patch. |
But looking at React specifically, we see https://github.com/facebook/react/blob/master/packages/react/src/React.js#L33 The top level package exports a single binding. All of what have been used as named imports are properties of this export. And then there is this great comment. |
React does not set the |
Yeah what I was getting at is that it seems anything but clear how the package intends to be consumed. With no
I really hope things do change but it is easy to get the impression that Node.js is supremely disinterested in these compatibility scenarios. |
@aluanhaddad as I understand it (and @weswigham indicated) - even if nodejs does not itself support named imports, they will still be supported when using typescript or babel. The code you would need to write will be automatically generated based upon the value of the __esModule flag. |
@frankwallis thank you for clarifying that. But since React doesn't set the flag, I would still argue that import React, Component from 'react'; even if supported, in transpilation, should be avoided because React does not seem at all interested in supporting it. Granted, that is a philosophical argument. I've been recommending import React from 'react';
const {Component} = React; Regarding named imports in Node.js, it implies a perpetual transpilation step even where not otherwise needed. Users seeing NodeJS advertising module support in Node.js 10, may be confused or surprised, unaware that they have taken such a dependency. More problematically, if they are authoring libraries, their documentation may not reflect the larger Node.js community's expectations. I'm not trying to say what ought to happen here, but rather that these usage patterns emerged at a time when Node.js was not expected to take the course they now have. As @Kovensky has noted many times, both TypeScript and Babel are highly permissive. I believe a strict conformance mode would be beneficial if only to set a baseline expectation of behavior in TypeScript. This whole space is currently very error prone. It is very easy to make mistakes and not even realize it until you are consumed by some complex downstream pipeline. |
Right. But whose strict behavior? Node doesn't know how the feature will stabilize yet, and browsers don't support any cross module-kind interop at all (yet do seem somewhat open to opening up their loader more in the future). As it turns out, es modules "arrived" with es6, but were the least standard new feature in the standard, since many behaviors are left unspecified. Browsers operate without interop, so how TS works today without a flag can emulate browser behavior pretty well (excepting the fact that declaration files allow you to confuse module kinds). The esmoduleinterop flag allows TS to emulate Babel's and webpack's interop behaviors, which, as should be obvious, many people have come to rely on. Whatever the node team finally lands on, we'll support; but I think the devs are doing a huge disservice to their existing community who was told they were using "es6 modules" with their transpiled code where they would "just be able to disable Babel" in the future when the JavaScript of the future arrives by not meeting developers where they are. They're clearly aware of this - they've introduced exceptions to the only-default approach for the builtin packages like fs and path, so they clearly know how awful it is right now (though such exceptions are gonna make our life more difficult). Really, we were all sold on the lie that we could reasonably smoothly transition into these things called modules without actually knowing how they'd behave in some very important cases. I'm a bit biased because I'm immersed in how the feature has evolved, but I'd not advocate for using modules with the intent of writing future-looking code anymore. Use them if you only want to target one runtime, or never remove a transpiler from your toolchain, but don't decieve yourself into thinking they'll ever be as portable as a chunk of code with a UMD definition block around it was in the last era of the web. |
Please can this issue be included in one of the upcoming milestones, or be opened to the community? Currently it causes a runtime error with no compiler error when consuming (for example) antd via its es-modules entry point. |
It would be great to get some clarity on this from the TS team. Here it is labelled as a bug, but in #22678 @RyanCavanaugh said it's the expected behaviour. The current behaviour is extremely surprising, and, AFAICT, should be considered a bug. i.e. Why should import _, { blah } from 'thing'
console.log(blah) and import _ from 'thing'
import { blah } from 'thing'
console.log(blah) produce different results? Because that's what's happening right now if an imported module doesn't specify I'm happy to contribute a fix for this, if there's agreement on what the behaviour should be. |
It's definitely a bug. People are getting hung up on tangential issues. We're just not counting the named imports for an importStar helper after we see the default import. |
I'm still having this problem. Not sure if it's the same thing, can you confirm? @weswigham Compile error: import API from 'apifn';
// TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
export default new API('https://google.com', []); This works: import { default as API } from 'apifn';
export default new API('https://google.com', []); Both I can't understand how those syntaxes are different, as I believe the default import syntax is part of the TC39 standard already or isn't it? Asking because this works perfectly with Babel. Only when I'm with projects with TypeScript that this confusion with The |
Definitely unrelated to this issue - this issue only affected emit, while that's a typechecking error. |
I'm still having this error when using esModuleInterop with When I use only named imports it works just fine import { Schema } from 'dynamoose';
console.log(Schema) // constructor function On the other hand, if I use default import, named import changes to undefined import dynamoose, { Schema } from 'dynamoose';
console.log(Schema) // Schema is undefined
console.log(dynamoose.Schema) // constructor function Both of these examples work with Babel 6.24 so I don't think it's correct behavior. It compiles just fine, but this could be a weakness of type definitions. For reference, in first example the generated code looks like this "use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const dynamoose_1 = require("dynamoose");
console.log(dynamoose_1.Schema); In second case the code generated looks like this "use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const dynamoose_1 = __importStar(require("dynamoose"));
console.log(dynamoose_1.Schema);
console.log(dynamoose_1.default.Schema); And that's what Babel generates 'use strict';
var _dynamoose = require('dynamoose');
var _dynamoose2 = _interopRequireDefault(_dynamoose);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
console.log(_dynamoose.Schema);
console.log(_dynamoose2.default.Schema); |
The second one (where This is how This should be defined as an |
Even if dynamoose export is technically incorrect, it's still very strange that adding new import type breaks the one used previously. If using named import isn't correct in this case, shouldn't it's value also be undefined in example 1 - when used without default import? I may be mistaken in thinking that |
This is an interesting one, I think in this situation it is because
Because typescript is using |
@frankwallis Do you think it's worth opening a new issue describing this problem? |
@rafsawicki I do believe this is a new issue, maybe @weswigham can comment? |
Shouldn't
It's very annoying to use right now. |
@AlexGalays that's probably just waiting on an updated react declaration file on definitely typed. |
Oh no, I meant it doesn't work at runtime. Nevermind it works now... debugging with approximative source maps were showing useState as undefined but it wasn't. |
FYI for those who got here from a google search seeing this error in intellisense in VS2019 despite their create-react-app based project building fine from the command line, I the error to go away by doing this (not sure if all these steps were necessary):
It tends to happen to me whenever I move a component into a different directory using the Solution Explorer. Sometimes just closing and re-opening the project makes it go away and sometimes I have to do the full clean/close/install/reopen dance. |
TypeScript Version: 2.8.0-dev.20180204
Search Terms: esModuleInterop
Code
I am seeing some unexpected behaviour when using
esModuleInterop
and importing from a commonjs module in 2 different ways at the same time:Expected behaviour:
Running the code outputs:
Actual behaviour:
Running the code outputs:
This is the javascript which is generated:
Related Issues:
None that I am aware of
The real-world code which is triggering this problem is here
The 'named' import and 'default' import work fine individually but do not work when they are both present in the same file.
The text was updated successfully, but these errors were encountered: