TypeScript error

TS2451: Two files declare the same top-level name

  • Error TS2451
  • Tested with TypeScript 5.9

error TS2451: Cannot redeclare block-scoped variable 'API_URL'. is reported when the same let, const or class name is declared twice in one scope. Inside a single file that is a plain mistake. Reported on two different files, it means the checker treats both files as scripts: files without import or export, whose top-level names are globals shared by every script.

Whether that is a bug depends on how the files run. Classic <script> tags without type="module" really do share one scope, and the second const API_URL throws a SyntaxError in the browser. Files that a bundler combines, or that load as modules, each have their own scope, and the checker just needs to be told.

At a glance

CodeTS2451
MessageCannot redeclare block-scoped variable '{0}'.
TypeScript's categoryerror
Listed underUnknown names and members
Depends on moduleDetection, module
In .js files Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file

The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} is filled in by tsc for each error.

What it looks like

cart.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: cart.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. cart.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checkout.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. checkout.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. cart.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. cart.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checkout.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. checkout.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. cart.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. cart.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checkout.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. checkout.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checked 21 October 2026
const API_URL = "/api/cart";error TS2451: Cannot redeclare block-scoped variable 'API_URL'.

let total = 0;error TS2451: Cannot redeclare block-scoped variable 'total'.
The first script declares API_URL and total at its top level.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "types": []
  },
  "include": ["*.js"]
}
checkout.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: cart.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. cart.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checkout.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. checkout.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. cart.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. cart.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checkout.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. checkout.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. cart.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. cart.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checkout.js(2,7): error TS2451: Cannot redeclare block-scoped variable 'API_URL'. checkout.js(5,5): error TS2451: Cannot redeclare block-scoped variable 'total'. checked 21 October 2026
const API_URL = "/api/checkout";error TS2451: Cannot redeclare block-scoped variable 'API_URL'.

let total = 0;error TS2451: Cannot redeclare block-scoped variable 'total'.
The second script declares the same two names, so both files fail.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "types": []
  },
  "include": ["*.js"]
}

What causes it

  • Two or more .js files without import or export declare the same top-level name, and the config's module settings make them scripts. That happens with module set to preserve, esnext or not set, under the default moduleDetection: "auto".
  • With module: "nodenext" (or node16 to node20) every .js file is a module, so this error does not appear across files.

How to fix it

If the files are modules, set moduleDetection to force

Anything a bundler or a modern runtime loads is a module. "moduleDetection": "force" in jsconfig.json tells the checker so for every file, and the same two files pass unchanged.

cart.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
const API_URL = "/api/cart";

let total = 0;
moduleDetection force: every file is its own module.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "types": [],
    "moduleDetection": "force"
  },
  "include": ["*.js"]
}

Or mark each file with an empty export

export {}; at the end of a file makes it a module without exporting anything. Only do this for files that are loaded as modules; adding export to a classic script makes the browser reject it.

cart.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
const API_URL = "/api/cart";

let total = 0;

export {};
An empty export makes a file a module.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "types": []
  },
  "include": ["*.js"]
}

For real classic scripts, keep the names apart

If the files are plain <script> tags, the clash is real. Rename one of the two, or wrap each file's code in a block (or an immediately invoked function) so its const and let names stay local.

cart.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
{
  const API_URL = "/api/cart";
  let total = 0;
  console.log(API_URL, total);
}
Real scripts that share the page's scope, each wrapped in a block.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • var and function declarations in two scripts do not clash with each other the same way (JavaScript allows redeclaring them), so the checker merges them instead; mixing var in one file with let in another is still TS2451.
  • The moduleDetection page shows the same two files under each setting.

Sources

Last reviewed 20 October 2026.