How to use normal imports and top-level await at the same time?
Asked Answered
D

1

11

I want to use imports (import x from y) and top-level awaits at the same time with ts-node. However if I change my tsconfig.compilerOptions.module to es2017 or higher as required by top-level awaits I get:

SyntaxError: Cannot use import statement outside a module

The fix for this is according to countless GH issues and SO questions to set tsconfig.compilerOptions.module to commonjs which in turn results in:

Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', or 'nodenext', and the 'target' option is set to 'es2017' or higher

How can I have both? There has to be a way...

tsconfig.json:

{
  "compilerOptions": {
    "declaration": true,
    "module": "esnext",
    "target": "es2017",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "sourceMap": true,
    "outDir": "dist",
    "skipLibCheck": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*.ts"]
}

package.json:

{
  "name": "x",
  "version": "0.0.1",
  "main": "main.js",
  "type": "module",
  ...
}

I am using Node LTS (v16.14.2) and TypeScript 4.6.3.

Dinnage answered 6/5, 2022 at 21:5 Comment(11)
You must use "type": "module" in your package.json when running a project using ESM.Coughlin
@catgirlkelly I just edited in the package.json. I already did that to no avail :/Dinnage
did you try this #71099811Teplitz
@y.rashi That answer is nonsense. I added a comment to it explaining why. But thank you for linking it anyway :)Dinnage
@Dinnage check out my answer, it seems to work on my machineTeplitz
@Dinnage Have you found a solution? I have the exact same problem.Preeminence
@Preeminence Unfortunately I had to pass on top-level awaitsDinnage
Does this have to be ts-node based? For example, what about using tsx?Robert
@Robert the question is over 2 years old, I'm looking for someone to explain why this is an issue more than a solution tbhDinnage
Ok. Well it's really not an issue with plain old node.js and typescript. I'll see what I can dig up. In fact I think I more or less answered it over here.Robert
Did you have an export statement in your top-level-await file? Often a script doesn't naturally need an export statement, but without it, it's not a module.Sitology
R
1

I believe this was primarily an issue for you because you used "moduleResolution: "node" in your tsconfig.json. Nowadays you're much better off setting "module": "nodenext" and "moduleResolution: "nodenext".

This answer has nothing to do with ts-node but only involves Node.js and TypeScript. You're better off using tsx which requires no configuration instead of ts-node.

Given the following project:

.
├── src/
│   ├── y.ts
│   └── main.ts
├── package.json
└── tsconfig.json

package.json

"type": "module"

tsconfig.json

{
  "compilerOptions": {
     "target": "ESNext",
     "module": "NodeNext",
     "moduleResolution": "Nodenext",
     "outDir": "dist"
  },
  "include": ["src"]
}

src/y.ts

const doSomethingAsync = () => Promise.resolve('something async')

export { doSomethingAsync }
export default 'y'

src/main.ts

import { doSomethingAsync } from './y.js'
import x from './y.js'

console.log(await doSomethingAsync())
console.log(x)

Now run ./node_modules/.bin/tsc to generate the build in dist/.

Now run node dist/main.js:

something async
y

The following versions of Node.js and TypeScript were used:

  • Node.js 22.1.0
  • TypeScript 5.4.5

If you need to use ts-node I can dig in further, but you didn't really explain why you needed that tool. Also, I would consider tsx over ts-node if you want to execute .ts files directly for whatever reason.

Using [email protected] you can get the same output by running:

./node_modules/.bin/tsx src/main.ts
Robert answered 9/5 at 20:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.