A JavaScript file is either a module, with its own top-level scope, or a script, whose top-level names are globals shared with every other script. moduleDetection decides which files TypeScript treats as modules. Under "auto", the default, a file is a module when it has import or export, or when its module format says so; "force" makes every file a module; "legacy" looks only for import and export.
It matters when files have no imports or exports. With module: "nodenext", Node's rules already make every .js file a module, ES or CommonJS. With bundler or preserve, two such files that each declare const label are scripts in one global scope, and TypeScript reports TS2451 in both. The option arrived with TypeScript 4.7.
At a glance
| Write | Leave it at "auto" with nodenext. With bundler or preserve, write "force" |
|---|---|
| On the command line | --moduleDetection |
| Type | string |
| Default in TypeScript 5.9 | auto |
| TypeScript's description | Control what method is used to detect module-format JS files. |
| In the quick-start setups |
|
The description is quoted from TypeScript's own option list (Apache-2.0); see the notices. The setup values are the checked configs on the quick-start pages.
Examples
const label = "a";error TS2451: Cannot redeclare block-scoped variable 'label'.
console.log(label);jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "preserve",
"types": [],
"moduleResolution": "bundler"
},
"include": [
"*.js"
]
} const label = "a";
console.log(label);jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "preserve",
"types": [],
"moduleResolution": "bundler",
"moduleDetection": "force"
},
"include": [
"*.js"
]
} When files really are scripts
Classic browser <script> tags without type="module" do share one global scope, so there TS2451 is a real collision and "auto" is right. Use "force" when every file is in fact a module: anything a bundler or a modern runtime loads.
Gotchas
tsc --initin TypeScript 5.9 writes"moduleDetection": "force", and Bun's recommended config sets it too.
Sources
- TSConfig Reference: moduleDetection · TypeScript
- TypeScript 4.7 release notes · TypeScript handbook
- TypeScript 5.9 release notes · TypeScript handbook
Last reviewed 20 October 2026.