Skip to content
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 is (should not be) required #1977

Closed
lirbank opened this issue Nov 15, 2018 · 11 comments
Closed

esModuleInterop is (should not be) required #1977

lirbank opened this issue Nov 15, 2018 · 11 comments
Labels
🧬 typings Relates to TypeScript changes or improvements.

Comments

@lirbank
Copy link

lirbank commented Nov 15, 2018

Building without enabling the esModuleInterop compiler option:

node_modules/apollo-server-core/dist/types.d.ts:4:8 - error TS1192: Module '"/node_modules/@types/ws/index"' has no default export.

4 import WebSocket from 'ws';
         ~~~~~~~~~

node_modules/apollo-server-express/dist/ApolloServer.d.ts:1:8 - error TS1192: Module '"/node_modules/@types/express/index"' has no default export.

1 import express from 'express';
         ~~~~~~~

node_modules/apollo-server-express/dist/ApolloServer.d.ts:2:8 - error TS1192: Module '"/node_modules/@types/cors/index"' has no default export.

2 import corsMiddleware from 'cors';
         ~~~~~~~~~~~~~~

While enabling esModuleInterop resolves the issue, I feel it should not be needed? Enabling it requires that all other imports where there is no default export to be updated too (import * as x vs import x). Making this change just for one package seems a bit intrusive. But I may be missing something here though.

Related issues: https://github.com/apollographql/apollo-server/issues?utf8=%E2%9C%93&q=is%3Aissue+esModuleInterop

I'm using the latest versions of everything:

    "apollo-server-express": "2.2.2",
    "express": "4.16.4",
    "graphql": "14.0.2",
    "graphql-tools": "4.0.3",
    "typescript": "3.1.6"
@KeithGillette
Copy link

I faced the same issue under [email protected] and found that setting "allowSyntheticDefaultImports": true, in the tsconfig.json compilerOptions also fixed the issue without the side-effects esModuleInterop caused for my project. Regardless, I agree that modifying the project's TypeScript configuration seems an unwarranted work-around.

@grantwwu
Copy link

grantwwu commented Nov 16, 2018

@KeithGillette allowSyntheticDefaultImports enables esModuleInterop (most of the time) https://www.typescriptlang.org/docs/handbook/compiler-options.html

But I agree - I feel like this is a regression - if anyone really wants me to/asks nicely - I can bisect to figure out when this regression was introduced.

@grantwwu
Copy link

Actually, upon review of the release notes https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html - this might not be an entirely bad idea.

That said, it is technically a breaking change, so... that said, fixing the breakage seems to be fairly trivial. (I hope? I am not an expert in ES module systems).

@lirbank
Copy link
Author

lirbank commented Nov 27, 2018

On a second thought, esModuleInterop is actually enabled by the default config provided by tsc --init, making it implicitly recommended to enable it. So maybe it's not all that intrusive to require it by a package as I first thought/felt.

These options are enabled by tsc --init (ts 3.1.6), nothing else:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true
  }
}

@abernix
Copy link
Member

abernix commented Apr 26, 2019

Relates-to or duplicated-by #1977, #2519, #2521 (and more).

Thanks so much for opening this originally. The important thing here is that the consuming project needs to adopt esModuleInterop: true. While enabling esModuleInterop within Apollo Server was more of a breaking change than intended (or even understood, at the time it was done), it's been almost a year now since it was first enabled. This flag first became available in TypeScript 2.7 and helps align TypeScript with the behavior chosen by Babel, Webpack, Rollup, React Native, etc. It's been the default configuration generated by tsc --init for quite some time and at this point we're intending to stick with esModuleInterop: true going forward.

It may be possible to avoid the type errors on an existing project by enabling allowSyntheticDefaultImports (which doesn't change the code emitted, just changes the type-checking behavior), but I haven't verified.

@Eru2008
Copy link

Eru2008 commented May 28, 2020

I faced the same issue under [email protected] and found that setting "allowSyntheticDefaultImports": true, in the tsconfig.json compilerOptions also fixed the issue without the side-effects esModuleInterop caused for my project. Regardless, I agree that modifying the project's TypeScript configuration seems an unwarranted work-around.

all works good, but when I try to start my server inside the docker, I had this "Error: socket hang up" in postman

and this in the console

Screenshot 2020-05-28 at 13 39 12

also, API doesn't work on the AWS

apollo-server-core: 2.14.0

Does someone have any solution?

@jasonkuhrt
Copy link

jasonkuhrt commented Jul 23, 2020

It's been the default configuration generated by tsc --init for quite some time and at this point we're intending to stick with esModuleInterop: true going forward.

It is one thing for a project to have this, another for a library. All downstream consumers are forced into this setting. To the extent that the library is consumed by other libraries this makes it harder for the community to adopt ES modules proper. I would generally say popular TS libraries should make effort to avoid forcing this option. If it is hard to do so in this case though then maybe not worth it for @abernix.

We're grappling with the fallout on nexus now, for example.

@ozyman42
Copy link
Contributor

ozyman42 commented Aug 29, 2020

I need to chime in here and say this logic seems sensible from first glance, but it is not correct

On a second thought, esModuleInterop is actually enabled by the default config provided by tsc --init, making it implicitly recommended to enable it.

The field is there for compatibility sake, not because CommonJS is somehow being prescribed as the preferred choice by TypeScript. TypeScript correctly follows the ES Module standard, which uses export and import, and knows nothing about importing using require or exporting via module.exports. So the behavior of TypeScript not compiling CommonJS modules when the esModuleInterop flag is absent means that TypeScript is only coupling itself to the official JS language and not becoming coupled to any Node-specific standards, and this is a good thing. Node.js !== JavaScript. TypeScript is meant to be a superset of JS, not of Node.js

Meanwhile, Node came about before the ES Modules standard was finalized, so they chose the CommonJS standard. Many npm packages were thus created which use module.exports = to ship their library. Therefore TypeScript had to add in this interop otherwise everyone would be forced into using require, which feels old and wrong to many hipsters.

Anyways, the future of the language is ES Modules, so everyone should be moving away from CommonJS. Heck even Node.js is starting to transition towards the industry standard ES Modules, and you can bet Deno officially recommends ES Modules and not CommonJS. I doubt Node will ever deprecate their CommonJS support due to their dogmatic adherence to backwards compatibility, but really they should since the JavaScript language has evolved to now have an official standard for modules.

ApolloGraphQL should only force all direct and transitive consumers to require esModuleInterop if they want to force as many projects as possible to be forcibly stuck in the past.

If this project wants to correctly not force esModuleInterop, all it needs to do is remove the field from its own tsconfig, then go and replace any offending import statement with a require. Bonus points for then going to the lib authors of those offending packages and nagging them to upgrade as well.

@amitlevy21
Copy link

Though esModuleInterop = true is the default for tsc --init, it is not the default value if its not specified in tsconfig.json https://www.typescriptlang.org/tsconfig#esModuleInterop

IMO, tsc --init provides that for backwards compatibility.
@abernix would you please reconsider this ? as @alexleung have made good points

@abernix
Copy link
Member

abernix commented Nov 17, 2021

I think it's certainly reasonable to consider re-visiting this since this is now several years old, but I would suggest opening a new issue or finding a issue that is open/still open. One hunch is that it might fit well into a larger story around shipping native ECMAScript alongside CJS, or might at least pair well with that work? (e.g., #5627).

@merrywhether
Copy link

Leaving this for future people who may wander upon this looking for guidance. The TS 2.7 release notes were mentioned upthread, but it's worth calling out that quote from those notes (emphasis theirs):

Note: The new behavior is added under a flag to avoid unwarranted breaks to existing code bases. We highly recommend applying it both to new and existing projects. For existing projects, namespace imports (import * as express from "express"; express();) will need to be converted to default imports (import express from "express"; express();).

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🧬 typings Relates to TypeScript changes or improvements.
Projects
None yet
Development

No branches or pull requests

9 participants