数値enumはObject.keys()で要素数が2倍になる(逆引きマッピングの副作用)
数値enumは、コンパイル後のJavaScriptオブジェクトに「メンバー名→値」だけでなく「値→メンバー名」の逆引きも自動的に持たせます。この副作用でObject.keys()を呼ぶと、メンバーの2倍の数のキーが返ってきます。
なぜエラーが出ないのか
出力: 4
(エラーなし)- TypeScriptは何も報告しません。型検査も構文も正しいためです
出力: 4- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 enum Status { 2 Active, 3 Inactive, 4 } 5 console.log(Object.keys(Status).length ); ^
出力: 4
数値enumはコンパイル後、メンバー名→値だけでなく値→メンバー名の逆引きも持ちます。Object.keys().lengthはメンバー数の2倍(4)になります。
直し方: length を filter(k => Number.isNaN(Number(k))).length にします。
広告
広告スロット(未設定)
パターン2
1 enum Level { 2 Low, 3 Mid, 4 High, 5 } 6 console.log(Object.keys(Level).length ); ^
出力: 6
Levelは3メンバーですが、逆引きマッピングの分も合わせてObject.keys().lengthは6になります。
直し方: length を filter(k => Number.isNaN(Number(k))).length にします。
パターン3
1 enum Direction { 2 North, 3 South, 4 East, 5 West, 6 } 7 console.log(Object.keys(Direction).length ); ^
出力: 8
Directionは4メンバーです。逆引き分も含めるとObject.keys().lengthは8になります。
直し方: length を filter(k => Number.isNaN(Number(k))).length にします。
よくある誤解
「enumはただのメンバー一覧を表す型のはず」という理解は、コンパイル後の実体までは説明していません。数値enumはコンパイル時にJavaScriptの通常のオブジェクトへ変換され、逆引き用のキーも一緒に埋め込まれます。
まとめ
数値enumはObject.keys()で要素数が2倍になる(逆引きマッピングの副作用)は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。