ある休日
娘(4歳)「パパ、ジャンケンしよ!?」
ワイ「おお、娘ちゃん」
ワイ「もうジャンケンができるようになったんか!」
ワイ「まだ関数を書くことくらいしかできんと思ってたわ〜」
よめ太郎「順番どうなってんねん」
ワイ「ほな、ジャンケンしようや!」
さっそく
娘「じゃあ、さっそくコードを見ていきたいんだけど・・・」
ワイ「ファッ!?」
ワイ「コード!?」
const hands = {
gu: "グー",
choki: "チョキ",
pa: "パー"
};
娘「まず↑こんなオブジェクトを定義してみたの」
ワイ「お、おお」
ワイ「ジャンケンの手やからhandsね」
娘「そう」
娘「それで」
娘「このhandsっていうオブジェクトから、型を作りたいの」
ワイ「オブジェクトから、型を、作る・・・?」
娘「"グー"か"チョキ"か"パー"っていう文字列しか許可しない・・・」
娘「Hand型っていうのを定義したいの」
ワイ「なるほどな」
ワイ「それなら・・・」
type Hand = "グー" | "チョキ" | "パー";
ワイ「↑これでええやん」
ワイ「このHand型は"グー"か"チョキ"か"パー"っていう文字列しか許可せえへんで!」
ワイ「union型いうやつやな」
ワイ「これで、間違って不正な値を代入せんようにできるで!」
ワイ「試しに"ピャー"という文字列を代入しようとすると・・・」
ワイ「ほら、VSCodeくんが赤い波線を表示してくれとる」
ワイ「エラーっちゅうことや」
娘「うん」
娘「でも、それは自分でunion型を書いてるだけでしょ?」
娘「handsっていうオブジェクトを元に、自動でunion型を作りたいの」
娘「このオブジェクトの持つ値は"グー"か"チョキ"か"パー"って分かり切ってるのに」
娘「わざわざ自分で"グー" | "チョキ" | "パー"っていうunion型を再定義したくないの」
ワイ「ハハハ」
ワイ「それは、残念ながら無理なご相談や」
ワイ「それに、オブジェクトの中身が書き換えられる可能性もあるやん」
ワイ「いくらconstで定義しても、オブジェクトは不変ではないんやから」
娘「でも前に、ハスケル子おねえちゃんができるって言ってたよ」
娘「オブジェクトからunion型を生成できるって」
ワイ「(ぐぬぬ)」
ワイ「ええと・・・」
娘「パパ、早く教えて?」
娘「私の稼働がもったいないの」
ワイ「稼働て・・・」
ワイ「(なんか、ワイも前にハスケル子ちゃんから教えてもらった気がするな・・・)」
必死に思い出す
ワイ「せや!」
ワイ「確か、順序としては・・・」
- まずkey名から
"gu" | "choki" | "pa"というunion型を作る。 - それを使って
"グー" | "チョキ" | "パー"というunion型を作る。
ワイ「↑こんな感じのはずや」
ワイ「あと・・・」
- keyof演算子
- typeof演算子
ワイ「↑この2つの演算子を使う、って言ってた気がするで」
ワイ「あと、まずは」
ワイ「handsオブジェクトの最後んところにas constをつけるんや」
const hands = {
gu: "グー",
choki: "チョキ",
pa: "パー"
} as const; // <- 追加
ワイ「↑こうやな」
ワイ「これでhandsオブジェクトは、中身を書き換えたりできない・・・」
ワイ「readonlyなオブジェクトになったんや」
娘「へぇ〜」
ワイ「このオブジェクトの持つ値は"グー"か"チョキ"か"パー"だって、確定してないとアカンからな」
娘「なるほどね」
ワイ「試しにVSCodeでhandsオブジェクトにマウスを乗せて」
ワイ「handsオブジェクトの型を確認してみ?」
娘「うん!」
娘「ほんとだ!」
娘「全部のkeyがreadonlyになってる!」
ワイ「これで、handsオブジェクトのkeyは常にgu、choki、paであること」
ワイ「valueは常に"グー"、"チョキ"、"パー"であること」
ワイ「それが型によって担保されたで!」
ワイ「ほんまの意味で定数になったってことやな」
娘「だからas constなんだね!」
typeofで型情報を見る
娘「パパ、それで」
娘「まずkey名から"gu" | "choki" | "pa"っていうunion型を作るんだっけ?」
娘「どうやってやるの?」
ワイ「まずな、handsオブジェクトの型情報があったやろ?」
娘「さっきVSCodeで確認したやつね」
ワイ「せや」
ワイ「それに別名を付けてやるんや」
type Hoge = typeof hands;
ワイ「↑こうや」
娘「へぇ〜」
娘「typeof handsっていうのが」
娘「handsオブジェクトの型情報、ってことなんだ!」
娘「それにHogeっていう別名を付けたんだね」
ワイ「せや」
ワイ「またVSCodeでマウスを乗せて確認してみ?」
ワイ「Hoge型に何が入っとるかを見るんや」
娘「うん!」
娘「ほんとだ」
娘「さっきのhandsオブジェクトの型情報と同じだ」
ワイ「そういう感じや」
keyofで、key名からunion型を定義する
ワイ「次はkeyofを使って」
ワイ「Hoge型に存在するkey名からunion型を作るんや」
type Keys = keyof Hoge;
ワイ「↑こうやな」
ワイ「マウスを乗せてKeys型の中身を見てみ?」
娘「あ、"gu" | "choki" | "pa"っていうunion型が出来てる」
ワイ「せや」
ワイ「こいつを元に、最終目的である・・・」
ワイ「"グー" | "チョキ" | "パー"というunion型を作るんや!」
娘「おお〜」
娘「今日のパパ、なんか凄い!」
またtypeofで型情報を見る
ワイ「次にやることは↓こんなイメージや」
type Hand = typeof hands["gu"];
娘「typeofでhands["gu"]の型を見て」
娘「その型にHandっていう別名を付けたんだね」
ワイ「せや」
ワイ「このHand型の中身は何やと思う?」
娘「ええと」
const hands = {
gu: "グー",
choki: "チョキ",
pa: "パー"
} as const;
娘「まず、handsオブジェクトの中身は↑こうだから」
娘「hands["gu"]の中身は"グー"でしょ?」
娘「だから、要はtypeof "グー"は何になるか、的な話でしょ?」
娘「じゃあ、型もそのまま"グー"なんじゃない?」
ワイ「正解や」
娘「念のためマウス乗せて見てみよ」
娘「ほんとだ。Hand型の中身は"グー"だ」
ワイ「せやせや」
ワイ「ほな、これはどうや?」
type Hand = typeof hands["gu" | "choki"];
娘「ええと」
娘「hands["gu" | "choki"]だから」
娘「値としては"グー"か"チョキ"が入ってるイメージだよね」
娘「じゃあ、型は"グー" | "チョキ"かな?」
ワイ「さすが、正解や」
娘「ほんとだ」
娘「ってことは・・・」
type Hand = typeof hands["gu" | "choki" | "pa"];
娘「↑こうすれば・・・」
娘「"グー" | "チョキ" | "パー"っていうunion型が作れるんだね!?」
娘「私の欲しかったやつ!」
ワイ「せや!」
娘「さっきkeyofで作ったKeys型が」
娘「"gu" | "choki" | "pa"っていうunion型だったから」
娘「それを使うんだね!」
ワイ「せや」
娘「つまり・・・」
type Hand = typeof hands[Keys];
娘「↑こうだね!?」
ワイ「さぁ〜、どうやろね」
娘「VSCodeでマウスを乗せて覗いてみる!」
娘「できた!」
娘「handsオブジェクトからHand型ができた!」
娘「これで、分かり切った"グー" | "チョキ" | "パー"を書かなくても」
娘「オブジェクトの値から、自動っぽく型が作れる!」
娘「パパ、ありがとう!」
ワイ「グヘヘ」
娘「試しに不正な値を代入してみよっと!」
娘「あ、ちゃんとエラー扱いになって、波線が表示されてる!」
娘「これで、Hand型の変数には」
娘「"グー"か"チョキ"か"パー"しか代入できないね!」
娘「不正な値は代入できないね!」
ワイ「せや!」
ワイ「ちなみに、いちいち型エイリアスを作らなくても」
type Hand = typeof hands[keyof typeof hands];
ワイ「↑こう書いても同じことやで」
ワイ「確認のためにVSCodeで見てみると・・・」
ワイ「↑ほらな」
娘「ほんとだ!」
ワイ「せやから、全体のコードとしては・・・」
const hands = {
gu: "グー",
choki: "チョキ",
pa: "パー"
} as const;
type Hand = typeof hands[keyof typeof hands];
ワイ「↑これだけや」
娘「オブジェクトを元にして型を定義する部分は」
娘「1行で書けちゃうんだね」
ワイ「せやで」
反復処理も可能
ワイ「元となるオブジェクトであるhandsが存在してる訳やから」
ワイ「Object.keys(hands)とかObject.values(hands)とかObject.entries(hands)みたいな」
ワイ「反復処理も可能やで!」
娘「いいね!」
娘「たまにそういうことしたい時あるもんね!」
よめ太郎「4歳児には無いやろ」
でも、enumでよくない?
娘「でもさ・・・」
娘「これenumで良くない?」
enum Hands {
gu = "グー",
choki = "チョキ",
pa = "パー"
}
娘「↑こんな感じで良くない?」
娘「enumでも反復処理はできるみたいだし」
ワイ「enumでもええねんけど」
ワイ「enumって何か、分かりにくいやん」
娘「そう?」
ワイ「enumという型に対してObject.entries()とかそういう」
ワイ「オブジェクト用のメソッドが適用できるのが何か腑に落ちんし」
ワイ「TSからJSに変換されると訳わからんコードになってまうし」
ワイ「人類には早過ぎたんや」
よめ太郎「人類っていうかお前個人の問題やな」
娘「でもまあ、分からなくもないね」
まとめ
- typeof演算子を使うと、型の情報を取得して別名をつけることができた。
- keyof演算子を使うと、型のkey名からunion型を生成できた。
-
typeof obj[keyof typeof obj]とする事で、オブジェクトからunion型を生成できた。 - 元となるオブジェクトが存在するため、
Object.keys()やObject.values()やObject.entries()などの反復処理も可能。
娘「↑こんな感じだね!」
ワイ「せや!」
その日の夜
ワイ「はあ、今日は娘ちゃんの役に立てて嬉しいわ」
ワイ「ワイでも技術的なことで役に立てることがあるんやな」
ワイ「滅多にないけどな」
ワイ「・・・せや!」
ワイ「もう一回さっきのコードいじって復習しとこ」
type Hand = typeof hands["guu"];
ワイ「↑こう書くと、確か・・・」
ワイ「Hand型の中身は"グー"になるはずやったな」
ワイ「VSCodeで確認してみよ」
ワイ「ファッ!?」
ワイ「アニーになっとるやないかい!」
よめ太郎「エニーや」
よめ太郎「アニーてそれ、ミュージカルやないかい」
よめ太郎「♪トゥモロー、トゥモロー言うてる場合か」
ワイ「そ、それより何で、型が**any**に・・・」
ワイ「・・・分かった!」
ワイ「VSCodeのバグや!」
よめ太郎「バグってんのはお前の脳みそや」
ワイ「いや、これはワイが正しい気がする」
よめ太郎「タイポしてんねん」
よめ太郎「"gu"を"guu"ってミスタイプしてんねん」
ワイ「ファッ!?」
よめ太郎「お前のはTypeScriptやなくてタイポスクリプトや」
ワイ「ぐぬぬ」
よめ太郎「VSCodeはな」
よめ太郎「TypeScriptを作っとるMicrosoftはんが作ってんねや」
よめ太郎「何でお前の方が正しいと思えたねん」
ワイ「テヘヘ・・・」
ワイ「ジョブズに怒られてまうわ」
よめ太郎「Microsoftはビル・ゲイツやろ・・・」
よめ太郎「エグいほど無知やな・・・」
ワイ「ぐぬぬ」
ワイ「いや・・・ワイはジョブズの方が好きやから・・・なんか・・・間違えてもうたわ」
よめ太郎「言い訳が苦し過ぎやろ」
ワイ「い、言い訳ちゃうわ」
ワイ「ほ、ほんまにジョブズが好きなんやワイは!」
よめ太郎「そうか」
よめ太郎「ほな、今から会わせたるわ・・・」
〜おしまい〜