【TypeScript】enumに特定の値が存在するかチェックする
更新日:2022/12/13
TypeScriptのenum型に特定の値やキーが存在するかどうかをチェックしてみます。
また値に対応するキーの取得も行います。
値の存在チェック
TypeScriptのenum型は、次のように定義します。
enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
enum DayWeek2{
SAN="日曜日",
MON="月曜日",
TUE="火曜日",
WEN="水曜日",
THU="木曜日",
FRI="金曜日",
SAT="土曜日",
}
次の関数で、enum型オブジェクトに特定の値が存在するどうかをチェックできます。
const valueExists = (enumObj:{},value:string|number):boolean=>
Object.entries(enumObj)
.some(e=>isNaN(Number(e[0])) && e[1]===value);
第一引数は enumObj:{} は、enum型オブジェクトです。
実際は、オブジェクトなら何でも受け付けます。
第二引数は文字列または数値で、チェックする値を指定します。
数値と文字列数値は別のものとして判定しています。
実行すると、次のようになります。
console.log( valueExists(DayWeek1,31) ); // 結果: true
console.log( valueExists(DayWeek1,3) ); // 結果: false
console.log( valueExists(DayWeek2,"火曜日") ); // 結果: true
console.log( valueExists(DayWeek2,"火") ); // 結果: false
■簡単な解説
まずはObject.entries()で、プロパティ名と値抜き出して配列化しています。
次のようなイメージです。
Object.values(DayWeek1)
→ [ ['0','SAN'] , ['1','MON'] , ...省略 , [ 'SAN': 0 ] , [ 'MON': 1 ] , ...省略 ]
Object.values(DayWeek2)
→ [ [ 'SAN': '日曜日' ] , [ 'MON': '月曜日' ] , ...省略 ]
各要素は [プロパティ名,値] です。
DayWeek1のプロパティ名が数値文字列(['0','SAN'] , ['1','MON'] , ...省略)の部分はTypeScriptが逆引き用に追加した要素なので、今回は除外する必要があります。
次にsome()メソッドは、コールバック関数が trueを返した時点で、結果がtrueになります。
コールバック関数が最後までtrueを返さない時は、結果がfalseになります。
コールバック関数の isNaN(Number(e[0])) は、不要な部分を除外しています。
この式はe[0]が数値に変換できない文字列のとき、trueになります。
e[1]===valueは、第二引数とプロパティの値を比較しています。
キーが存在するかチェック
enum型オブジェクトに特定のキーが存在するどうかのチェックは、考え方は前項の値の存在チェックと同じです。
some()メソッド内での比較を、プロパティ値からプロパティ名に変更するだけです。
次のようになります。
const keyExists = (enumObj:{},keyName:string):boolean=>
Object.entries(enumObj)
.some(e=>isNaN(Number(e[0])) && e[0]===keyName);
この関数の実行例です。
console.log( keyExists(DayWeek1,"SAN") ); // 結果: true
console.log( keyExists(DayWeek1,30) ); // 結果: false
console.log( keyExists(DayWeek2,"TUE") ); // 結果: true
console.log( keyExists(DayWeek2,"火曜日") ); // 結果: false
値に一致するキーを取得
次は特定の値と値が一致するプロパティの名前を取得します。
次のようになります。
const getKeyFromValue = (enumObj:{},value:string|number):string|undefined=>
(Object.entries(enumObj)
.find(e=>isNaN(Number(e[0])) && e[1]===value)
?? [undefined]
)[0];
find()メソッドは前々項のsome()と同じ用途ですが、真偽値ではなくて配列の要素を返します。
そして、返ってきた配列の[0]を返しています。
しかしfind()メソッドは、コールバック関数が最後までtrueを返さない時の結果はundefinedです。
undefined[0] とするとエラーになってしまいます。
そこで登場するのが ?? です。
これはNull 合体演算子と呼ばれるもので、?? の左辺が null または undefined の時に右辺値を、そうでない時に左辺値を返します。
今回は、右辺に[undefine]という配列を置くことで、[undefine][0]になるのでエラーになるのを回避できています。
関数の実行例です。
console.log( getKeyFromValue(DayWeek1,31) ); // 結果: THU
console.log( getKeyFromValue(DayWeek1,3) ); // 結果: undefined
console.log( getKeyFromValue(DayWeek2,"火曜日") ); // 結果: TUE
console.log( getKeyFromValue(DayWeek2,"火") ); // 結果: undefined
どちらかというとJavaScript
今回のコードはTypeScriptではなくて、JavaScritですね。
TypeScriptのenum型をJavaScriptで操作できるのは、トランスパイル時に実体(オブジェクト)を生成するコードが挿入されるからです。
enumの実体
次のコードをトランスパイルしてみます。
enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
enum DayWeek2{
SAN="日曜日",
MON="月曜日",
TUE="火曜日",
WEN="水曜日",
THU="木曜日",
FRI="金曜日",
SAT="土曜日",
}
生成されたファイルには、次のようなコードが挿入されます。
var DayWeek1;
(function (DayWeek1) {
DayWeek1[DayWeek1["SAN"] = 0] = "SAN";
DayWeek1[DayWeek1["MON"] = 1] = "MON";
DayWeek1[DayWeek1["TUE"] = 2] = "TUE";
DayWeek1[DayWeek1["WEN"] = 30] = "WEN";
DayWeek1[DayWeek1["THU"] = 31] = "THU";
DayWeek1[DayWeek1["FRI"] = 32] = "FRI";
DayWeek1[DayWeek1["SAT"] = 33] = "SAT";
})(DayWeek1 || (DayWeek1 = {}));
var DayWeek2;
(function (DayWeek2) {
DayWeek2["SAN"] = "\u65E5\u66DC\u65E5";
DayWeek2["MON"] = "\u6708\u66DC\u65E5";
DayWeek2["TUE"] = "\u706B\u66DC\u65E5";
DayWeek2["WEN"] = "\u6C34\u66DC\u65E5";
DayWeek2["THU"] = "\u6728\u66DC\u65E5";
DayWeek2["FRI"] = "\u91D1\u66DC\u65E5";
DayWeek2["SAT"] = "\u571F\u66DC\u65E5";
})(DayWeek2 || (DayWeek2 = {}));
DayWeek1は、少し分かりにくいですね。
次のコードは、二つに分けることができます。
DayWeek1[DayWeek1["SAN"] = 0] = "SAN";
↓
DayWeek1["SAN"] = 0;
DayWeek1[0] = "SAN"; // 数値インデックス
2行目は逆引き用の数値インデックスを作成しています。
const enumは対象外
enumの前にconstを記述すると、トランスパイル時に実体(オブジェクト)を生成するコードを挿入しません。
その代わりに、値の置き換えが行われます。
TypeScriptコード
// enumをconstで定義
const enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
console.log( DayWeek1.THU );
トランスパイル後のコード
// enumをconstで定義
console.log(31 /* DayWeek1.THU */);
実体が無いため、特定の値が存在するかどうかなどのチェックするには少し強引な方法が必要です。
preserveConstEnumsオプションをtrueにすると、constを記述してもコードが挿入されます。
const enumで値が存在するかチェック
const enumは実体が生成されないので、enumの値を全て抜き出して比較します。
const enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
const valueExists = (value:number):boolean=>{
switch(value){
case DayWeek1.SAN:
case DayWeek1.MON:
case DayWeek1.TUE:
case DayWeek1.WEN:
case DayWeek1.THU:
case DayWeek1.FRI:
case DayWeek1.SAT:return true;
default: return false;
}
}
console.log( valueExists(DayWeek1.THU) );
配列にして比較する方法もあります。
const valueExists = (value:number):boolean=>{
const values = [DayWeek1.SAN,DayWeek1.MON,DayWeek1.TUE,
DayWeek1.WEN,DayWeek1.THU,DayWeek1.FRI,DayWeek1.SAT] as number[];
return values.indexOf(value) >= 0;
}
結局のところオブジェクトを生成していますが、配列の値に意味を持たせるという点では、const enumを使用する意義があります。
ただenumの要素数が膨大だったり、追加や削除の可能性があるとバグ発生の原因になりそうです。
この場合は const enumは避けた方がいいかもしれませんね。
更新日:2022/12/13
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。