TL;DR — クイック修正
ファイルの先頭に空の export {} を追加するだけです。この一行でESモジュールに変換され、TypeScriptのDOM組み込みグローバルとの競合が解消されます。
// ファイルの先頭に追加する
export {};
const name = 'John';
エラー内容
Cannot redeclare block-scoped variable 'name'.
const name = 'John' という何の変哲もない宣言を書いただけなのに、TypeScriptがエラーを出します。ファイルに他の name は存在しません。どこにも問題はなさそうなのに、すぐに赤い波線が表示されます。
根本原因
import や export 文が一つもない .ts ファイルは、TypeScriptによってESモジュールではなくグローバルスクリプトとして扱われます。そのため、ローカル変数はTypeScriptの組み込み型宣言とスコープを共有することになります。
lib.dom.d.ts の深部には、TypeScriptが次のように定義しています:
// lib.dom.d.ts 内 (TypeScriptの組み込み定義)
declare var name: string;
あなたの const name はこの宣言に真っ向からぶつかります。この競合を引き起こしやすい変数名の例:
name—window.namedocument—window.documentlocation—window.locationevent—window.eventstatus—window.statusorigin—window.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 を読み込まなくなり、name や document などがグローバルとして存在しなくなります。
注意: 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` を有効にする
モジュール不要のクイックスクリプトブロックスコープ `{ }` で囲む

