[Typescript] The Verbatim Module Syntax in TSConfig
To fix the CommonJS export issue, we need to make a change to our tsconfig.json
file.
Add the verbatimModuleSyntax
setting and set it to true
:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"verbatimModuleSyntax": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"isolatedModules": true,
"outDir": "./dist"
}
Over the years, there have been a few attempts to resolve this problem, aiming to make TypeScript stricter about CommonJS exports. The verbatimModuleSyntax
setting is the current approach to accomplish this, and it's highly recommended for all tsconfig.json
files to ensure cleaner output.
Let's examine the error TypeScript throws if we use a top-level export
with verbatimModuleSyntax
enabled:
export const example = () => { // red squiggly line under `export`
return "hello!";
};
// hovering over export shows:
A top-level export modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled.
This error message indicates that we shouldn't use the export
keyword directly within our .cts
files. Instead, we need to refactor our code. First, we remove the top-level export
and use export = { example }
to properly export the function:
const example = () => {
return "hello!";
};
export = {
example
};
Now, the emitted JavaScript code closely mirrors the TypeScript code when it is compiled down to module.exports
.
Importing CommonJS Modules
When importing, there is a little bit of a wrinkle to be aware of.
Consider this example:
const iWantToImportThis = () => {
return "hello!";
};
export = {
iWantToImportThis,
};
When importing this in a separate file, the syntax is a combination of import
and require
:
import secondFile = require('./second-file.cjs');
const example = () => {
return "hello!";
};
secondFile.iWantToImportThis
This unusual syntax is necessary to ensure type safety in CTS modules.
If we were to use a typical const
declaration with require
, TypeScript would infer the type of secondFile
as any
since require
could potentially return anything. That's why the combined import
and require
syntax is necessary.
To recap, the verbatimModuleSyntax
setting in tsconfig.json
enforces proper CommonJS syntax in .cts
files. This approach ensures that the emitted JavaScript code closely resembles the TypeScript code, which will make your life easier.