switch文でconstは使えない!警告の正体と解決法
はじめに
こんにちは、トラマトです!
現在、Canvas APIを使ってパラメータをいじれる幾何学アートジェネレーター(Github)を作っています。
しかし、描画のアルゴリズムをswitch文で切り替えようとしたとき、こんなエラーが出てきました。
Unexpected lexical declaration in case block
え、caseの中で変数宣言しちゃダメなんすか?と最初困惑したのですが、原因と解決法、JavaScriptの仕様に納得したので備忘録としてまとめます!
遭遇したエラー
幾何学模様の形をmodeという変数で切り替えるために、こんなコードを書いていました。
このエラーの正体はTypeScriptのコンパイルエラーではなく、ESLint(コードの問題点を指摘してくれる静的解析ツール)のno-case-declarationsというルールによる警告です。
switch (mode) {
case 'Polygon': // 多角形モード
const sides = Math.max(3, waves);
const a = Math.PI / sides;
// ...描画処理...
break;
case 'Heart': // ハート型モード
const r = baseRadius * 0.1;
// ...描画処理...
break;
}
原因: switch文は全部ぶち抜く仕様だった
ちょっと前に大学でC言語を触っていた感覚だと、「caseごとに処理は別なんだから、変数も別物なのでは?」と思ってしまいます。
しかしなんと、JavaScript(TypeScript)の仕様では、switch文全体で1つのスコープを共有していたのです。
// ↓この { から
switch (mode) {
case 'Polygon':
const sides = 3;
const a = 10;
break;
case 'Heart':
const r = 5; // ← 同じ空間に sides, a, r が混在してしまっている
break;
// ↓この } までが同じ1つの空間
}
constやletはブロックスコープ({}の中だけで有効な変数) を作るための宣言です。
しかし、このままではPolygon用の変数もHeart用の変数も同じ部屋に散らかっている状態になり、関数名の衝突などのバグの温床になります。
だからしっかりとUnexpected lexical declarationという警告を出してくれていたわけですね〜
解決法: caseごとにブロックを作る
原因が「同じ部屋だから」なので、解決法は至ってシンプル。以下のようにcaseの中に壁を作ってあげればOKです。
switch (mode) {
case 'Polygon': { // ← { を追加してブロックスコープを作る!
const sides = Math.max(3, waves);
const a = Math.PI / sides;
break;
} // ← } で閉じる
case 'Heart': { // ← ハート用にも個室を作る
const r = baseRadius * 0.1;
break;
} // ← こっちも閉じる!
}
caseの中身を{ }で囲むだけ!
これだけで独立したブロックスコープが生まれるので、例えばPolygonの部屋で作ったsidesという変数が他の部屋に影響を与えなくなります。エラーも綺麗に消え去りますね。
まとめ
-
switch文の各caseは、デフォルトでは同じスコープを共有している - これにより、そのまま
constやletを使うとスコープが混ざりエラーを吐く -
caseの中身を{ }で囲うだけで独立したスコープとなり、安全な変数宣言が可能になる
基礎的なことですが、TypeScriptのエラーから「なぜダメなの??」を深堀りすると、言語についてより詳しくなれるような気がするので面白いですね。
引き続き、幾何学アートジェネレーターの完成を目指してゴリゴリ書きまくっていきます!
それでは!
Discussion