Top-level await expressions are only allowed when the module option is set to es2022

avatar
Borislav Hadzhiev

Last updated: Feb 29, 2024
4 min

banner

# Table of Contents

  1. Top-level await expressions are only allowed when the module option is set to es2022
  2. Using the await keyword inside of an async function
  3. Using top-level await via the command line with tsc
  4. Updating your tsconfig.json to support top-level await
  5. Using esrun to resolve the issue

# Top-level await expressions are only allowed when the module option is set to es2022

To solve the error "Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext'", use the await keyword in an async function or set the module option to es2022 and target to es2017 in your tsconfig.json file.

top level await expressions are only allowed

Here is an example of how the error occurs.

index.ts
// ⛔️ Error: 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.ts(1378) const result = await Promise.resolve(42); console.log(result); export {};

I used the export {} line to make the file an ES module because await expressions are only allowed at the top level of a file that is a module, otherwise, an error is issued.

index.ts
const result = await Promise.resolve(42); console.log(result); // ⛔️ Error: 'await' expressions are only allowed at the top level // of a file when that file is a module, but this file has no imports // or exports. Consider adding an empty 'export {}' to make this file a module.ts(1375)
The code for this article is available on GitHub

# Using the await keyword inside of an async function

One way to solve the error is to use the await keyword inside of an async function.

index.ts
async function example() { const result = await Promise.resolve(42); console.log(result); // 👉️ 42 // 👉️ result is available here } example();

using await keyword inside async function

The code for this article is available on GitHub

# Using top-level await via the command line with tsc

You can also use the tsc command line utility to enable top-level await.

However, note that tsc ignores your tsconfig.json configuration file when you supply a file name to compile.

Therefore, you could use command line arguments to explicitly set the target and module options.

shell
npx tsc -t es2022 -m es2022 --moduleResolution node --outDir build src/index.ts

Make sure to update the outDir option (build/ in the example) and the input file (src/index.ts) if necessary.

Suppose you have the following file that makes use of top-level await.

index.ts
const result = await Promise.resolve(42); console.log(result); export { };
The code for this article is available on GitHub

The command will output an index.js file in your build/ directory.

# Updating your tsconfig.json to support top-level await

Alternatively, you can update your tsconfig.json file to support top-level await in your project.

To enable top-level await in your TypeScript project, open your tsconfig.json file and set:

  • module to es2022 (or higher)
  • target to es2017 (or higher) or ESNext

Note that you may also have to set the option in your tsconfig.app.json file if using Angular, or whichever config file is used in your build process.

tsconfig.json
{ "compilerOptions": { "target": "es2022", "module": "es2022", "outDir": "build", "esModuleInterop": true, "lib": ["ES2017", "DOM", "ES2021.String", "es2020"], "preserveConstEnums": true, "moduleResolution": "node", "strict": true, "inlineSourceMap": true, "types": ["node"] }, "include": ["src/**/*"], "exclude": ["node_modules"] }
The code for this article is available on GitHub
Restart your IDE and development server after making the changes.

Now I am able to use top-level await in my index.ts file.

index.ts
const result = await Promise.resolve(42); console.log(result); export {};

You can now use npx tsc to compile your .ts file into a .js file that will be placed in your build/ directory at build/index.js.

shell
npx tsc && node build/index.js

If you get an error "To load an ES module, set "type": "module" in the package.json or use the .mjs extension.", you also have to set type to module in your package.json file.

package.json
{ "type": "module", // ... rest }

Now you can run your file that contains top-level await.

shell
npx tsc && node build/index.js
The code for this article is available on GitHub

If you use the include or files options, make sure the file you are using top-level await in is included in your project and is being tracked by TypeScript.

The module option sets the module system for the project.

The difference between having module set to es6 and es2022 (or esnext) is that es2022 adds support for top-level await.

The target option changes which JavaScript features are down-leveled and which are left intact.

For example, arrow functions get converted to regular functions if your target is ES5 or lower.

When you set your target option to es2017, you are essentially saying that you don't want to support browsers that don't support es2017 features.

Depending on your environment, this might not matter. For example, if you only target modern browsers or are writing server-side code, you'll probably be just fine.

Just make sure the emitted JavaScript is able to run in the environments where you intend to run your application.

# Using esrun to resolve the issue

Alternatively, you can use the esrun package to resolve the issue.

The esrun package runs out of the box and will execute your TypeScript files without using a bundler.

First, make sure you have the module installed.

shell
npm i -g esrun
The code for this article is available on GitHub

Alternatively, you can use the module by prefixing it with npx.

Suppose you have the following src/index.ts file.

src/index.ts
const result = await Promise.resolve(42); console.log(result); export { };

The file makes use of top-level await.

Now you can run the file as follows.

shell
npx esrun src/index.ts

Running the command will output 42.

If you still get an error, try to set module to esnext in your tsconfig.json.

tsconfig.json
{ "compilerOptions": { "module": "esnext" } }

You can also use the esrun package in watch mode.

shell
npx esrun --watch src/index.ts
The code for this article is available on GitHub

The command will listen for changes in your src/index.ts file and will rerun the code every time you save your changes.

You can read more about using esrun on the package's NPM page.

If you still get errors, I've created a minimal GitHub repository with a working example.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.