TypeScript: Very confusing message when user doesn't realize they're in a CommonJS context
Found from an issue I was diagnosing for @mpodwysocki.
Today, if you use "module": "nodenext"
or "module": "node16"
, you might try to import from a package with ESM files and get the following error message
Module 'yadda/file.js' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.
This is extremely confusing. The fix is typically to add the following field to the package.json
that corresponds to the importing file with the error:
"type": "module"
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 2
- Comments: 16 (9 by maintainers)
I’m on Typescript 4.9.3 and this doesn’t seem to be fixed yet.
It is VERY confusing. I am glad that I found this thread, and hope the head message can be updated soon.
--moduleResolution bundler
How about this.
If the file is
.cts
or.cjs
extension:Related diagnostics:
If the file does not have an
.mts
or.mjs
extension and there is notype
field set inpackage.json
Related diagnostics:
If the file does not have an
.mts
or.mjs
extension and there istype
field set inpackage.json
Related diagnostics:
This becomes especially confusing because it’s an ESM import you’re writing (so the error is actively wrong to say it “cannot be imported using this construct”—it can, with the right package configuration). The error only makes sense when you know the import is being transpiled to
require()
, and even that’s confusing because it’s not actually a problem with the source code as written but instead the JS output.I don’t think it’s appropriate to rule out dynamic import in any of these scenarios. I’m planning to use a single head message for all of them:
and optionally attaching either
or
(We cannot place a related diagnostic on the package.json file itself because it’s not a source file.)
That will not do the trick when importing ESM.
Should do the trick too.
You’re a life saver. Thank you. I did google, but I guess I figured it would’ve been something in the tsconfig. I appreciate your help.
Note that this isn’t a valid fix if the tsconfig specifies
"module": "node"
- then the dynamic import gets converted torequire()
too.