Typescript: Cannot use import statement outside a module

Update: Because this answer is getting significant views I have updated it to better show the available solutions for this problem in 2022.


The error means Node found an import statement in a file that it does not consider an ECMAScript (ES) module. Adding "type": "module" to package.json will tell Node you are using ES modules, but then you will need to tell the TypeScript compiler to emit this type of module by setting "module": "es2015" or higher (for example: "es2020") in tsconfig.json . If you want to emit CommonJS modules (require), set "module": "commonjs".

In case you don’t want to set the module system at the project level, there are more fine-grained options. Files with the .mjs extension are always treated as ES modules, while files with .cjs are always treated as CommonJS modules. As of TypeScript 4.5 it is possible to use the .mts and .cts extensions as well and have the compiler emit .mjs or .cjs files, respectively.

The two systems are partially compatible. For example, it is possible to import a CommonJS module into an ES module with a default export:

// in an ES module
import thing from "./main.cjs";

The other way around. an ES module may be imported into a CommonJS module with dynamic import (ES2020 features are needed for this to work):

// in a CommonJS module
const thing = await import("./main.mjs");

Original answer (2020):

Adding "type": "module" to package.json will tell Node you are using ES2015 modules, which should get rid of the error, but then you will need to tell Typescript to generate this type of module by setting "module": "es2015" instead of "commonjs" in tsconfig.json.

This however causes a problem with the current code because although you are using an ES6 import {} statement you are exporting using the commonJS module.exports = {} syntax, and Node’s ES module loader will have an issue with it. There are two ways to deal with it:

  • Keep the module.exports but tell Node to interpret this file as commonJS by giving it a .cjs extension.
  • Change the export statement to ES2015 syntax: export function execute(…)..

The first option could get a bit tricky because the compiler will output .js files and you’d have to change it to .cjs all the time (as far as I know). With the second option you should be able to run the file with Node (including the –experimental-modules flag for versions < 13.8).

If you absolutely need to use commonJS, perhaps it is better to install the type definitions for Node: @types/node and change the import to commonJS format: require('abc') and keep the rest of the settings as they are (though you can add "type": "commonjs" to package.json to be explicit).

Leave a Comment