jquery TypeScriptの変数定義時のコンパイルエラー :ブロック スコープの変数 ‘name’ を再宣言することはできません。ts(2451)

2023年2月8日

TypeScript で name などの特定の識別子を使って変数を定義したり、プロジェクト内で別ファイルなのに同じ変数名を定義するとコンパイルエラーが発生します(ファイルがモジュールでない場合)。

index.ts

const name: string = 'foo';
console.log(name);
% npx tsc   //コンパイルを実行        
src/index.ts:1:7 - error TS2451: Cannot redeclare block-scoped variable 'name'.

1 const name: string = 'foo';
        ~~~~
  node_modules/typescript/lib/lib.dom.d.ts:17878:15
    17878 declare const name: void;
                        ~~~~
    'name' was also declared here.

Found 1 error in src/index.ts:1

以下は VS Code でのスクリーンショットですが、name という名前の変数を宣言した時点で以下のエラーが発生します。

ブロック スコープの変数 'name' を再宣言することはできません。ts(2451)
lib.dom.d.ts(17878, 15): ここでは 'name' も宣言されました。

コンパイルを実行すると、以下のようなコンパイルエラーになります。

error TS2451: Cannot redeclare block-scoped variable 'name'.

エラーになる理由

TypeScript をインストールすると、JavaScript や DOM の型定義が付属していて、その中の DOM API の型定義ファイル(lib.dom.d.ts)に、name がすでに定義されているために発生します(同じ変数を再宣言できない)。

関連ページ:TypeScript 環境の構築(lib.d.ts)

そのため、name の他にも TypeScript をインストールした際に付属される型定義ファイルで宣言されている変数名と同じもの(length、locationなど)は同様のエラーになります。

また、プロジェクト内で別ファイルなのに同じ変数名を定義するとコンパイルエラーが発生する理由も、TypeScript においてファイルがスクリプトとして扱われる場合(ファイルがモジュールでない場合)、トップレベルに定義した変数や型のスコープはファイル内だけではなくプロジェクト全体になるため、このエラーが発生します。

エラーが出ないようにするには以下のような方法があります。

  • 変数名を異なる名前(name 以外)に変更する
  • export {}; を追加して、ファイルをモジュールとして扱う
  • ブロック内に記述する
  • tsconfig.json で設定

解決方法1

変数名を異なる名前(name 以外)に変更します(付属の型定義ファイルや同じプロジェクト内の別のファイルで定義されていない識別子に変更します)。

const uname: string = 'foo';  //name 以外に変更

console.log(uname);

解決方法2

TypeScript においてファイルがスクリプトとして扱われる場合、トップレベルに定義した変数のや型のスコープはファイル内だけではなくプロジェクト全体になるため、このエラーが発生します。

モジュールとして扱われる場合はモジュール内で定義された変数のスコープはそのモジュール内になります。

TypeScript の場合、コード中に import や export が使われている場合は自動的にモジュールとして扱われるので、export {}; を追加することでそのファイルをモジュールとすることができます。

以下のように空の変数をエクスポートする export {}; を追加して、ファイルをモジュールとします。

const name: string = 'foo';

console.log(name);

export {}; //追加

解決方法3

グローバル変数にならないようにブロック化します。但し、変数のスコープはブロック内に限定されます。

{
  const name: string = 'foo';

  console.log(name);
}

解決方法4

TypeScript 4.7 で追加されたコンパイラーオプションの moduleDetection を "force" に設定します。

moduleDetection は、ファイルがスクリプトであるかモジュールであるかを TypeScript がどのように判断するかを制御します。

tsconfig.json で compilerOptions の moduleDetection を "force" に設定すると、すべてのファイルがモジュールとして扱われるようになります。

tsconfig.json

{
  "compilerOptions": {
    ・・・中略・・・
    "moduleDetection": "force"  // すべてのファイルをモジュールとして扱う
  }
}

または、tsconfig.json で module を TypeScript 4.7 で追加された node16 にして、package.json の "type" を "module" にします。tsconfig.json で moduleResolution を指定している場合は、こちらも node16 にします。

tsconfig.json

{
  "compilerOptions": {
    "target": "es2022",
    "module": "node16",  // node16
    "moduleResolution": "node16",  // node16(不要であれば省略)
    ・・・中略・・・
  }
}
package.json

{
  "type": "module",
  ・・中略・・・
}

参考サイト: