1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

コンパイルされた Typescript の列挙型( Enums )のコードを見てみる

Posted at

書かないこと

Enums の是非。Union 型使うべきとかは一切触れません。

Enums とは

列挙型は、関連する値の集合を編成する方法です。
https://typescript-jp.gitbook.io/deep-dive/type-system/enums

enum People {
  a,
  b,
  c
}

console.log(People.a)
// 出力結果: 0

console.log(People[0])
// 出力結果: a

Object で言うkey を引数に value を取得したいとき、逆に value を引数に key を取得したい時はちょくちょくあります。上記のコードだとシンプルすぎますが、例えば、カレンダーを Enum で持つ場合はけっこう実用的かと思います。
(コードを簡略化するならJanuary = 1 だけ書けば、February以降は自動でインクリメントされるため数値の代入は不要)。

enum Month {
  January = 1,
  February = 2,
  March = 3,
  April = 4,
  May = 5,
  June = 6,
  July = 7,
  August = 8,
  September = 9,
  October = 10,
  November = 11,
  December = 12
}

console.log(Month.May)
// 出力結果: 5

console.log(Month[5])
// 出力結果: May

Javascript で Enums が欲しいときは...

言うてしまえば、key と value どちらも重複して持つ Object があれば Enums と完全に同じことができます

const People = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  a: 0,
  b: 1,
  c: 2
}

console.log(People.a)
// 出力結果: 0

console.log(People[0])
// 出力結果: a

余談ですが

自分が Typescript ではなく、Javascript を書いているときに Enums が欲しいときは、Array を元に Object を作ったりします。key と value が 3つとかなら、そのまま書いてもいいですがね。

const sampleArray = [
  'a',
  'b',
  'c'
]

const sampleObj = Object.assign(
  ...Object.entries(sampleArray).map(value => ({ [value[1]] : value[0] })),
  ...Object.entries(sampleArray).map(value => ({ [value[0]] : value[1] }))
)

console.log(sampleObj)
// 出力結果: { '0': 'a', '1': 'b', '2': 'c', a: '0', b: '1', c: '2' }

console.log(sampleObj.a)
// 出力結果: 0

console.log(sampleObj[0])
// 出力結果: a

Enums も結局は同じことをしている

以下のように定義した Enums をコンパイルして、Javascript のコードにすると結局はやっていることが同じであることがわかります(コンパイルされて Javascript になるので当たり前のことを書いています汗 )。

// コンパイル前
enum People {
  a,
  b,
  c
}
// コンパイル後
var People;
(function (People) {
    People[People["a"] = 0] = "a";
    People[People["b"] = 1] = "b";
    People[People["c"] = 2] = "c";
})(People || (People = {}));

console.log(People);
// 出力結果: { '0': 'a', '1': 'b', '2': 'c', a: 0, b: 1, c: 2 }

出力結果を見れば分かる通り、key と value どちらも重複して持つ Object が出力されており、これが Enums であった、ということでした。

コンパイル後のコードは意味わからん

Enum のコンパイル後のコードの出力結果を見たところで、目的の8割達成やねんけど、どうにもコンパイル後のコードの意味がわからんので、解明だけしておきます。

var People;
(function (People) {
    People[People["a"] = 0] = "a";
    People[People["b"] = 1] = "b";
    People[People["c"] = 2] = "c";
})(People || (People = {}));

関数の中身から見ていくと、a を key にして、0 という value の Object を作っています。

People["a"] = 0
// 出力結果 : 0

代入演算子( = ) は、右から解釈されるので、出力対象は People["a"] となり、結果は 0 が出力される。

Javascript の解釈される順序は以下を参照。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

次を見てみると、People["a"] = 00 を返しているから、結局は以下の解釈になります。

// People[People["a"] = 0] = "a";
People[0] = "a";

ここまで来ると、以下のコードでどんな Object が生成されているかがわかります。

People[People["a"] = 0] = "a";
People[People["b"] = 1] = "b";
People[People["c"] = 2] = "c";
// 出力結果: { '0': 'a', '1': 'b', '2': 'c', a: 0, b: 1, c: 2 }

次に関数の部分を見てみると、
あんまり自分は使わないが、以下は即時実行関数として実行されている。
https://developer.mozilla.org/ja/docs/Glossary/IIFE

// 即時実行関数の構文
// (function () {
//   ...省略
// })();
(function (People) {
    People[People["a"] = 0] = "a";
    People[People["b"] = 1] = "b";
    People[People["c"] = 2] = "c";
})(People || (People = {}));

以下の部分は、即時実行関数にわたす引数として PeoplePeople = {} は定義されています。そして、PeoplePeople = {} は OR 条件(||)で評価されます。

})(People || (People = {}));

PeoplePeople = {}がそれぞれどのように評価されているか、を見ていくねん。
(People || (People = {})) に渡される People は、即時実行関数の前に定義されている var People; になります。定義されて値が設定されていない変数は、undefined なので、false として扱われます。(Boolean(undefined) を実行すると実際に確認可能です)

以下は Javascirpt が false として扱う一覧で、undefined の他に 0 とかがあります。
https://developer.mozilla.org/ja/docs/Glossary/Falsy

var People;
(function (People) {
  ...省略
})(People || (People = {}));
// People の中身は undefined なので、false として評価される

(People = {}) は true として扱われます。(Boolean({}) を実行すると実際に確認可能です)
外側の()は、Object を返す場合の構文で、評価の対象ではないので、実際に評価されるのは People = {} になります。空の Object は true として扱われます。
https://developer.mozilla.org/ja/docs/Glossary/Truthy

var People;
(function (People) {
  ...省略
})(People || (People = {}));
// People = {} は trueとして評価される

これで function (People)People に渡される値がわかりました。
評価としては以下のように解釈されているため、true である右側の引数が function (People) に渡されています。ってことは、People = {} ってことやなー。

var People;
(function (People) {
  ...省略
})(false || true);
// People = {} が即時実行関数の引数として渡される

これで全ての要素を分解しきったので、以下のコードが腹に落ちました。
即時実行関数の引数として渡された空の Object にせっせと key と value をセットしている、という実行内容でした。

var People;
(function (People) {
    People[People["a"] = 0] = "a";
    People[People["b"] = 1] = "b";
    People[People["c"] = 2] = "c";
})(People || (People = {}));

結局わからないこと

(その1) コンパイルした後のコードってこれでよくないのか??
People || は、var Peopleである限り、false 扱いやから、いる意味がよくわからない。

var People;
(function (People) {
    People[People["a"] = 0] = "a";
    People[People["b"] = 1] = "b";
    People[People["c"] = 2] = "c";
})(People = {});

(その2) コンパイルした後のコードってこれでよくないのか??
即時実行関数で実行する必要あるのか?

var People;
People[People["a"] = 0] = "a";
People[People["b"] = 1] = "b";
People[People["c"] = 2] = "c";

Typescript 本体のコードとか色々と見たらわかるのかもしれないから、見てみよう。

まとめ

以上、終わり!

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?