Last updated: Feb 29, 2024
Reading time·4 min
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.
Here is an example of how the error occurs.
// ⛔️ 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.
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)
await
keyword inside of an async
functionOne way to solve the error is to use the await
keyword inside of an async
function.
async function example() { const result = await Promise.resolve(42); console.log(result); // 👉️ 42 // 👉️ result is available here } example();
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.
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
.
const result = await Promise.resolve(42); console.log(result); export { };
The command will output an index.js
file in your build/
directory.
tsconfig.json
to support top-level awaitAlternatively, 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.
{ "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"] }
Now I am able to use top-level await
in my index.ts
file.
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
.
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.
{ "type": "module", // ... rest }
Now you can run your file that contains top-level await.
npx tsc && node build/index.js
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.
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.
esrun
to resolve the issueAlternatively, 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.
npm i -g esrun
Alternatively, you can use the module by prefixing it with npx
.
Suppose you have the following src/index.ts
file.
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.
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
.
{ "compilerOptions": { "module": "esnext" } }
You can also use the esrun
package in watch mode.
npx esrun --watch src/index.ts
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.
You can learn more about the related topics by checking out the following tutorials: