223

I have a .ts file in node js (latest version of node.js for 07.10.19) app with importing node-module without default export. I use this construction: import { Class } from 'abc'; When i run the code, i have this error: Cannot use import statement outside a module.

In the network i see many solutions for this problem (for .js), but it not helps to me, maybe because i have typescript file. Here's my code:

import { Class } from 'abc';
module.exports = { ...
    execute(a : Class ,args : Array<string>){ ...

Here's my tsconfig.json:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",

    "strict": true
  }
}
4
  • Are you running this in a browser? Is the import statement the first line in your file? Commented Oct 7, 2019 at 16:58
  • 1
    Can you please post your tsconfig.json file? When you compile in Typescript, you can determine which type of modules it produces, and the valid types may differ depending on which environment (browser/NodeJS) and which other modules (require vs import) you use. Just to give you a sense of how complicated this is, Node has some documentation about import vs require and how to make them work together. Commented Oct 7, 2019 at 17:25
  • 2
    If you are using module.exports syntax, you're probably not in an ES6 module. Commented Oct 7, 2019 at 18:17
  • Ok, i can't use require because Cannot find namespace 'abc' when using construction execute(a : abc.Class... Commented Oct 8, 2019 at 15:26

11 Answers 11

167

If you happen to be using ts-node, you can set some compiler options in tsconfig.json specifically for ts-node to use.

{
  "ts-node": {
    // these options are overrides used only by ts-node
    // same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable
    "compilerOptions": {
      "module": "commonjs"
    }
  },
  "compilerOptions": {
    "module": "esnext"
  }
}

Then you can just execute your script pretty easily:

$ ts-node my-script.ts
Sign up to request clarification or add additional context in comments.

8 Comments

Oh yeah, I've got this issue with hardhat when integrating it in a nextjs app where both came with different tsconfigs.
This is should be accept answer! I can run it without type as "module" and change ts to mts
This is the way, especially in projects like create-react-app that come with a built in tsconfig!
simply passing --compilerOptions worked for my use-case. Adding it to the tsconfig did not work. Thanks for the comment in your example!
Lost 5 hours to find this answer. God bless you.
|
140

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).

4 Comments

I get the error that: "ReferenceError: require is not defined in ES module scope, you can use import instead" for the bundle that webpack generates? How do I prevent this?
note for myself: if you never write module.exports then you just need to read the first paragraph.
I made vice-versa! I changed in tsconfig.json to "module" : "CommonJS" and removed "type":"module" in package.json and it helped me :D
Why did I even remove "type": "module" :D
20

Make sure your "main" field in package.json is pointing to the compiled index.js and not the index.ts

Comments

9

Solution if "type": "module" is NOT an option

  • rename .ts to .mts
  • run ts-node --esm file.mts

here's my tsconfig.json for reference:

{
  "compilerOptions": {
    "incremental": true,
    "target": "ESNext",
    "module": "ESNext",
    "allowJs": true,
    "checkJs": false,
    "outDir": "./dist",
    "sourceMap": true,
    "strict": true,
    "alwaysStrict": true,
    "moduleResolution": "node",
    "esModuleInterop": true
  },
  "include": ["**/*"]
}

1 Comment

for me this results in TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".mts" for src/database/scripts/migrate.mts
3

I had very similar issue. I had to install nodemon and always start script through nodemon index.ts

yarn add -D nodemon

package.json

"scripts": {
   "start": "nodemon index.ts"
}

or to specify the file from the command line

package.json

"scripts": {
   "nodemon": "nodemon $1"
}

1 Comment

This seems to work for me (with ts-node installed, using import statements without `"type": "module" in package.json.
1

There can be a lot of of possibilities for getting,

SyntaxError: Cannot use import statement outside a module

while running typescript.

In my setup, I was importing a module called, AbModule (AbModule has class AbClassName) from my script testab.ts.

testab.ts had import stmt,

import {AbClassName} = 'testAdapterDir/AbModule.po.ts';

However, AbModule had all *.ts files and there were *.js files were not present. fix is,

You may get errors in running the below code but you can safely ignore them.

cd AbModule path
tsc *.ts

Now AbModule should contain compiled files all *.js too. Now run testab.ts and noticed that the mentioned error does not exist anymore,

Comments

1

I had similar issue with one Node TypeScript project. I needed some features of ESM, and when changed "target" and "module" in tsconfig.json to ESNext, I've got an error "Cannot use import statement outside a module".

The solution helped me was:

  1. implement "type": "module" in package.json
  2. implement "target": "ESNext" and "module": "NodeNext" in tsconfig.json (afterwards I checked that "module": "ESNext" worked as well by the way)
  3. change all import statement in *.ts files adding js extension like this import { AccountGroup } from "./AccountGroup.js" (despite that actual code in TypeScript project before compilation is inside AccountGroup.ts file)

Solution was found here and was inspired by this source

Comments

0

For Monorepo

Issue: A workspace A outputs "Typescript: Cannot use import statement outside a module" because of a dependent typescript workspace B.

Steps:

workspace B

  1. Define a composite: true in the compileOptions (tsconfig) in workspace B

{
  ...,                 
  "compilerOptions": {
    ...,
    "composite": true
  }
}

  1. Set the main field to the index.js of in the outDir for the package.

workspace A

  1. Define the references key in the root of the tsconfig to take an array of object with path key and a value that references the relative path to workspace B tsconfig.

{
 ...,
  "references": [
    {"path": "../../workspace B"}
  ]
}

  1. Build your project with tsc -b and run your project with node pointing to the built index.js in workspace A. e.g. node build/index.js

You are welcome.

Comments

0

In tsconfig.json change the module to "module": "es2016" or later then this issue: Can not import statement outside module: Will not appear on running the node app.js files.

target

Comments

-1

That's what helped me to get rid of the issue when running the ts-node index.ts command:

tsconfig.json

{
  "compilerOptions": {
    "module": "node16" // or node18 or nodenext
  }
}

Comments

-2

Node v20 & type: module & module: ESNext

It seems like Node introduced a breaking change in v20. See here.

You can use node --loader ts-node/esm src/index.ts but people complain of high resource usage. See here.

I ended up using npx tsc; node build/index.js as suggested here.

2 Comments

Before downvoting, please explain the problem so I can improve my answer. Thank you
Still getting that error unfortunately

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.