こんにちは!フロントエンド・アーキテクチャの現場を長年渡り歩いている者です。
TypeScriptを触り始めると、「なんだか厳格な門番がたくさんいるなあ」「JavaScriptなら一瞬で書けるのに、なんで型エラーで止まっちゃうんだろう…」と、途方に暮れることってありますよね。痛いほどよく分かります。私も昔は、赤色の波線が出るたびにパソコンをそっと閉じたく衝動に駆られていました。
でも、大丈夫ですよ。今日お話しする`keyof`(キーオフ)型演算子は、そんなTypeScriptがあなたを困らせるためではなく、「あなたのコードを強力に守るための最強の相棒」に変わる瞬間を体験できる、最高に面白い機能です。
今日は、難しい専門用語はなるべく置いておいて、身近な例えと一緒にゆっくり解きほぐしていきましょう!
—
渋滞するお買い物リストで考えてみよう
まずは、TypeScriptの型とか難しい話はちょっと置いておいて、私たちの日々の生活を思い浮かべてみてください。
例えば、近所のスーパーにお買い物に行くとき、スマホのメモアプリにこんな「買うものリスト」を作ったとします。
- たまご
- 牛乳
- パン
このメモアプリには、「このリストにある項目以外は買っちゃダメ!」という暗黙のルールがありますよね。「テレビ」とか「高級スポーツカー」は、このお買い物リストの項目(キー)としては存在しません。
これをTypeScriptのオブジェクトとして表現してみましょう。
// お買い物リスト(オブジェクト)の定義
const shoppingList = {
tamago: 130, // たまご(200円)
gyunyu: 180, // 牛乳(180円)
pan: 150, // パン(150円)
};
JavaScriptの世界であれば、このリストのキー(`”tamago”`、`”gyunyu”`、`”pan”`)を取り出すには `Object.keys(shoppingList)` なんて書いたりしますよね。
では、「このオブジェクトにあるキーの名前だけを抽出した『型の世界の一覧リスト』がほしい!」と思ったとき、どうすればいいでしょうか?
そこで登場するのが、今回の主役である `keyof` 演算子 です。
—
`keyof` って一体なにをしてくれるの?
一言で言うと、`keyof` は「オブジェクトの型から、そのプロパティ名(キー)を抜き出して、ユニオン型(選べる型)として作り出してくれる魔法の杖」です。
百聞は一見に如かず。実際にコードで見てみましょう。
// お買い物リストの「型」を定義します
type ShoppingList = {
tamago: number;
gyunyu: number;
pan: number;
};
// ★ここが keyof の出番!
// ShoppingList のキー(部屋の名前)を全部集めてこい!という意味になります
type ShoppingKeys = keyof ShoppingList;
// つまり、上の行は裏側でこう解釈されています:
// type ShoppingKeys = “tamago” | “gyunyu” | “pan”;
すごいです! `keyof ShoppingList` と書くだけで、TypeScriptは自動的に `”tamago” | “gyunyu” | “pan”` というユニオン型(〜または〜または〜)を作り出してくれました。
もし将来、リストに `”ringo”`(りんご)を追加したくなったらどうでしょう?
元の `ShoppingList` の型に `ringo: number;` を足すだけで、`ShoppingKeys` の中身も自動的に `”tamago” | “gyunyu” | “pan” | “ringo”` にアップデートされます。
わざわざ手動で書き直す必要なんてありません。これが、保守性の高いコードを書くための第一歩です!
—
実務でどう使うの?「安全な値の取り出し関数」を作ってみよう
「なるほど、キーの名前を型として取り出せるのは分かったけど、実務の現場でどう役立つの?」という疑問が湧いてきますよね。
よくある実例として、「オブジェクトと、そのキーを受け取って、安全に中身を取り出す関数」を作ってみましょう。
もし `keyof` を使わないで素朴に書こうとすると、「そんなキーは存在しないかもしれないよ!」とTypeScript怒られてしまいます。そこで `keyof` の出番です。
// お弁当のメニューと価格のデータ
const bentoMenu = {
makunouchi: 680,
karaage: 500,
salmon: 580,
};
// 型エイリアスを使って、メニューのキーの型を定義
type BentoMenuType = typeof bentoMenu; // まずオブジェクトから型を作る
type BentoKey = keyof BentoMenuType; // “makunouchi” | “karaage” | “salmon” のこと
/
- 指定したお弁当の価格を安全に取得する関数
- @param menu お弁当のデータ
- @param key どのお弁当にするか(キー)
/
function getBentoPrice(menu: BentoMenuType, key: BentoKey): number {
return menu[key]; // keyof のおかげで、「確実に存在するキーだ」と保証されているためエラーにならない!
}
// 使い方
const price = getBentoPrice(bentoMenu, “karaage”); // 500 が返ってくる!
console.log(price);
// 【もし間違ったキーを指定しちゃったら…?】
// getBentoPrice(bentoMenu, “steak”);
// ❌ 爆速でエラーになります!「”steak” なんてメニューはありませんよ!」と親切に教えてくれます。
どうですか? 「絶対に存在するキー以外は受け付けない!」という堅牢な関数が、いとも簡単に作れちゃいました。これぞTypeScriptの真骨頂です。
—
つまずきやすいポイント:配列やプリミティブ型との付き合い方
ここで、初心者の人がよくハマる「ちょっとした罠」についてもシェアしておきますね。
「オブジェクトじゃなくて、文字列や配列に対して `keyof` を使ったらどうなるんだろう?」という疑問です。
実は、`keyof` はオブジェクトだけでなく、JavaScriptのさまざまな値(というか型)に対して使うことができます。例えば、配列や文字列です。
// 配列の型に対して keyof を使うと…?
const fruits = [“apple”, “banana”, “orange”];
type FruitKeys = keyof typeof fruits;
// びっくりするような結果になります:
// 0 | 1 | 2 などの数値インデックスだけでなく、
// length や push, map といった「配列が持っている便利なメソッドの名前」まで全部列挙されます!
「えっ、配列の要素の型(`string`)が欲しかったのに、なんかすごい量の文字が出てきたんだけど……!」とパニックになるのが、ここでの定番のつまずきポイントです。
大丈夫です、深呼吸してください。
もし配列の中身自体の型がほしいときは、`keyof` ではなく、別の構文(`number` でインデックスアクセスする `typeof fruits[number]` など)を使います。
`keyof` はあくまで「その型が持っている『プロパティの名前(キー)』を取り出すもの」。この基本さえ頭の片隅に置いておけば、もう迷うことはありません!
—
まとめ:`keyof` はコードの「オートロック」
今日のまとめです!
1. `keyof` とは:オブジェクト型から、そのプロパティ名(キー)を文字のユニオン型として一網打尽に抽出しれくれる演算子。
2. 最大のメリット:元のオブジェクトが変わっても、型が勝手に追従してくれるので、手動で書き直すミスがなくなる。
3. 実用性:存在しないキーの指定をコンパイル時にビシッと防いでくれるので、バグの温床を未然に断てる。
TypeScriptの型システムは、一見すると冷たくて厳しいルールのように思えるかもしれません。でも、使いこなせるようになってくると、まるで自分のコードの背中をいつも預けられる「信頼できる相棒」のように頼もしく感じられてきます。
最初は「ふーん、そんな便利なものがあるんだ」くらいの軽い気持ちで、まずは小さなオブジェクトで `keyof` を試してみてくださいね。あなたのTypeScriptライフが、少しでも快適で楽しいものになりますように!

コメント