Repository navigation
Import assignment not allowed when module format is ES modules #22321
Description
TypeScript Version: 2.7.2
Search Terms: import assignment modules import = require types
Code
When targeting CommonJS, I can pull in values and types from a CommonJS module like so:
import Koa = require('koa');Which generates a normal require statement:
const Koa = require('koa');Actual behavior:
When targeting MS modules, import assignment triggers the warning:
[ts] Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
The suggestions aren't available though, because
- This CJS module isn't ES module compatible, it uses an
module.exports =style export. - An ES
importstatement will emit andimportstatement, and I need it to emitrequire()to use with@std/esm(more on this latter).
The only way to generate a require() is to use one without import assignment, but then we have to jump through major hoops to import the types:
// Import just the type under a different name so there's not a clash in the value namespace
// Make sure you import only types so this isn't emitted!
import _Koa from 'koa';
// Alias just the type back to the class name
type Koa = _Koa;
// Re-declare the static interface because there's no way I know of to extract the
// static interface of a class in this situation.
interface KoaConstructor {
new(): Koa;
}
// Normal require(), and cast to the static type
const Koa = require('koa') as KoaConstructor;This is obviously pretty cumbersome.
Expected behavior:
I think supporting import assignment when emitting ES modules is the easiest solution. Just continue to emit:
const Koa = require('koa');Obviously, we don't know how CJS/ESM interop is going to behave quite yet. The possibilities range from:
- Not supporting CJS in
importstatements (probably unlikely due to Node.js team preferences) - CJS modules only having a default export with the value of
module.exports(most likely) - Somehow supporting named exports (seems difficult to impossible).
Regardless of the CJS interop support, it will at least possible to still use require() (maybe still a global, maybe via import.meta.require).
But even with interop, using require() is necessary for type-checking because existing typings describe what's returned by require(), not what would be returned from the CJS interop proposals. That is, existing typings do not describe an ES module with a default export. TypeScript may also need a way to transform typings under various interop schemes, but that's a separate issue.
So, currently at least, we need to emit require() instead import and also bring in the types. Import assignment does exactly this.
Another option could be to offer an easier way to import just the type of a module and cast the require() result, but that seems to only more verbosely describe what import assignment already does. Essentially it would be allowing this:
import * as _Koa from 'koa';
const Koa = require('koa') as _Koa;Without the error that's generated from the import:
[ts] A namespace-style import cannot be called or constructed, and will cause a failure at runtime.
Playground Link: BTW, there's no option in the playground to set the "module" compiler option.
Related Issues:
#19500
Activity
DanielRosenwasser commentedon Mar 4, 2018
Maybe I've missed something, but can you use
--esModuleInteropand use a default import for koa?justinfagnani commentedon Mar 4, 2018
Maybe I'm missing something: I can't use an
importat all because Koa isn't an ES6 module. I need to emit a require for it.justinfagnani commentedon Mar 4, 2018
Maybe it's confusing because I didn't include an
importin the examples:I want this:
to output:
justinfagnani commentedon Mar 4, 2018
Also,
esModuleInteropdoesn't have any effect when targeting ES modules, right?DanielRosenwasser commentedon Mar 4, 2018
Ah, duh, right.
DanielRosenwasser commentedon Mar 4, 2018
But in that case, your loader (
@std/esm) will do the heavy lifting for you, so I think you should be able to useallowSyntheticDefaultImportswith"module": "esnext"xialvjun commentedon May 29, 2018
besides
koa, there is some other use case:I have a lib
my_lib, and it depends onupper_lib:But I want
my_libsupport tree shaking, so:typescript-bot commentedon Jun 12, 2018
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
justinfagnani commentedon Jun 12, 2018
Daniel Rosenwasser (@DanielRosenwasser) I think this issue is still relevant, especially as module interop is defined on the node side of things. Basically all the Definitely Typed typings assume that you can have named exports from CJS modules, which 1) doesn't work if you have to require() CJS and 2) doesn't work if CJS doesn't have named exports, but default exports the
exportsobject.So I think import assignment should work in module output, so you can
import Koa = require('koa')with current types properly, and it seems likely that there will need to be some way to interpret types differently based on how the loader works, ie if node doesn't allow named exports from CJS, then interpretimport koa from 'koa'as essentiallyimport * as koa from 'koa'.ekilah commentedon Dec 20, 2018
as someone who doesn't really know much about any of this CJS vs ES Modules nonsense, I've arrived here after enabling
esModuleInteropwhen trying to figure out how to import an npm package & it's types I don't control.is there any update, 6 months later, on the proper way to import a package that uses statements like
export = PackageName; export namespace PackageName{...}in it's typings file? I would like to use the typings AND the functionality from one import (since two imports clash with the same name), but as the OP Justin Fagnani (@justinfagnani) says, it seems like that's not possible withesModuleInteropenabled?again, I don't really have a clear and full understanding of all the complexities, I'm just trying to use a library.
for context/ an example:
DanielRosenwasser commentedon Dec 20, 2018
Monroe Ekilah (@ekilah) with
esModuleInteropyou wantekilah commentedon Dec 20, 2018
Daniel Rosenwasser (@DanielRosenwasser) sorry, I left that case out. Still doesn't work:
and here's the top of the types file for reference: