TypeScript「Cannot redeclare block-scoped variable」グローバル型との競合を修正する

beginner🔵 TypeScript2026-04-22| TypeScript 4.x / 5.x、任意のOS(Windows / macOS / Linux)、Node.jsプロジェクト、React/Vue/Angularアプリ

Error Message

Cannot redeclare block-scoped variable 'name'.
#typescript#ブロックスコープ#グローバル#lib.dom#isolatedModules#export

TL;DR — クイック修正

ファイルの先頭に空の export {} を追加するだけです。この一行でESモジュールに変換され、TypeScriptのDOM組み込みグローバルとの競合が解消されます。

// ファイルの先頭に追加する
export {};

const name = 'John';

エラー内容

Cannot redeclare block-scoped variable 'name'.

const name = 'John' という何の変哲もない宣言を書いただけなのに、TypeScriptがエラーを出します。ファイルに他の name は存在しません。どこにも問題はなさそうなのに、すぐに赤い波線が表示されます。

根本原因

importexport 文が一つもない .ts ファイルは、TypeScriptによってESモジュールではなくグローバルスクリプトとして扱われます。そのため、ローカル変数はTypeScriptの組み込み型宣言とスコープを共有することになります。

lib.dom.d.ts の深部には、TypeScriptが次のように定義しています:

// lib.dom.d.ts 内 (TypeScriptの組み込み定義)
declare var name: string;

あなたの const name はこの宣言に真っ向からぶつかります。この競合を引き起こしやすい変数名の例:

  • namewindow.name
  • documentwindow.document
  • locationwindow.location
  • eventwindow.event
  • statuswindow.status
  • originwindow.origin

修正方法

方法1:ファイルに export {} を追加する(推奨)

一行追加するだけで、副作用もありません。空のexportによってTypeScriptはこのファイルをモジュールと認識し、独立したスコープが与えられます。

// ファイルの最上部に記述する
export {};

const name = 'Alice';
const location = 'Tokyo';

console.log(name, location);

以降のコードはそのまま動作します。ブラウザアプリ、React/Vue/Angularプロジェクトなど、DOMに触れるあらゆる場面で使える定番の修正方法です。

方法2:tsconfigから lib.dom を削除する(Node.jsのみ)

CLIツールやバックエンドスクリプトを作成している場合、DOM型は必要ないはずです。lib 配列から dom を削除しましょう:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"]
  }
}

dom を除外すると、TypeScriptは lib.dom.d.ts を読み込まなくなり、namedocument などがグローバルとして存在しなくなります。

注意: React、Vue、Angularプロジェクトでは絶対に行わないでください。dom を削除すると、すべてのブラウザAPI型が即座に壊れます。

方法3:競合している変数名を変更する

最も手早い解決策は変数名の変更です。衝突しない名前を選びましょう:

// 変更前(window.name と競合する)
const name = getUserName();

// 変更後
const userName = getUserName();
const displayName = getUserName();

新しいコードには問題なく使えます。ただし、40箇所で使われている変数を含むリファクタリングの最中だと、この方法はあまり魅力的ではありません。

方法4:tsconfigで isolatedModules を有効にする

Vite、esbuild、Babelを使っている場合、これらのバンドラーは各ファイルを個別にトランスパイルします。isolatedModules: true によってTypeScriptの動作をそれに合わせられます:

{
  "compilerOptions": {
    "isolatedModules": true,
    "target": "ESNext",
    "module": "ESNext"
  }
}

このフラグを有効にすると、importやexportがなく明示的にモジュールでないファイルがある場合にTypeScriptが警告を出します。症状を追いかけるのではなく、問題の原因で気づくことができます。

方法5:ブロックスコープまたはIIFEでコードを囲む

ts-node で簡単な使い捨てスクリプトを実行する場合、exportの追加は大げさに感じるかもしれません。ブロックスコープやIIFEも同様に有効です:

// ブロックスコープ
{
  const name = 'Bob';
  console.log(name);
}

// またはIIFE
(function () {
  const name = 'Bob';
  console.log(name);
})();

この方法でも機能します。ただし、一度限りのスクリプト以外では export {} に比べてイディオム的ではありません。

修正の確認

TypeScriptコンパイラをチェックのみのモードで実行します(出力ファイルなし、エラーのみ確認):

# TypeScriptコンパイラをno-emitモードで実行してエラーのみ確認する
npx tsc --noEmit

# tscがグローバルにインストールされている場合
tsc --noEmit

出力が何もなければコードは問題ありません。VS Codeでは、ファイルを保存した瞬間に赤い波線が消えます。再起動は不要です。

新しいファイルでこのエラーが起きやすい理由

作りたての .ts ファイルにはimportがまだないため、最初のキーストロークからグローバルスクリプトモードになっています。実際の import(例えば別ファイルからユーティリティを読み込む)を追加した瞬間、TypeScriptは静かにそのファイルをESモジュールモードに切り替え、競合は自然に消えます。

このエラーが集中して発生しやすいケース:

  • importを持たないスタンドアロンのユーティリティスクリプト
  • TypeScriptの設定ファイル
  • 単独で実験的に書いているクイックスニペット
  • 直近のリファクタリングでimportをすべて削除したファイル

まとめ

状況最適な修正方法


ブラウザ / React / Vue アプリファイルに `export {}` を追加する
Node.js / CLIツールtsconfigの `lib` から `dom` を削除する
Vite / esbuild プロジェクト`isolatedModules: true` を有効にする
モジュール不要のクイックスクリプトブロックスコープ `{ }` で囲む

Related Error Notes