みなさん、こんにちは!日夜フロントエンドのコードと向き合い、時には深夜のバグ退治に頭を抱え、時には美しい設計がバシッと決まってガッツポーズをしている、現役のチーフアーキテクトです。
Web制作やフロントエンドの開発を学び始めて、HTML/CSSやJavaScriptが少しずつ形になってくると、次に挑戦したくなるのが TypeScript(タイプスクリプト) ですよね。
でも、TypeScriptを学び始めると、
「なんだか呪文のような英語や記号が増えて難しそう…」
「型を定義するだけで、なんでこんなに苦労しなきゃいけないの?」
と、ちょっぴり心が折れそうになる瞬間はありませんか?
大丈夫、その気持ち、ものすごくよく分かります。 実は、今でこそ「アーキテクト」なんて名乗っている私自身も、昔は型エラーの赤い波線を見るたびに「もうJavaScriptのままでいいじゃん!」と泣きそうになっていました。
今回は、そんなTypeScriptの「最初の高い壁」に見えるけれど、仕組みさえ分かれば「型を自動で生み出してくれる超便利な魔法の道具」になる、`keyof` 演算子 と Mapped Types(マップドタイプス) の組み合わせについて、どこよりも優しく、身近な例え話を交えて解説します。
一歩ずつ、お茶でも飲みながらゆっくり進めていきましょう!
—
1. 今回の主役たちを、身近な例えでイメージしよう!
難しいコードに入る前に、まずは今回の主役である2つの技術を、私たちの日常にあるものに例えてみます。
あるところに、「お買い物リスト」があったとします。
// 買いたいもののリスト
const shoppingList = {
apple: “リンゴ(3個)”,
milk: “牛乳(1本)”,
bread: “食パン(6枚切り)”
};
このリストを元にして、TypeScriptの世界で魔法をかけます。
① `keyof` 演算子 = 「お買い物スタンプ」
`keyof` は、オブジェクトの「名前(キー)」だけをきれいに型抜きしてくれる型抜きクッキーの型です。
上のリストでいうと、中身の「リンゴ」や「牛乳」は無視して、`”apple”`、`”milk”`、`”bread”` という名前だけを抜き出して、「この3つのどれかしか使っちゃダメだよ!」というルール(ユニオン型)を作ってくれます。
② Mapped Types = 「たい焼きの金型」
Mapped Typesは、「すでにある型をベースにして、新しい型をいっぺんに作り出すたい焼きの金型」です。
例えば、「お買い物リストの品名はそのまま使いたいけれど、中身(値)を全部『買ったかどうか(`true` か `false`)』のチェックボックスに変えたいな」という時に、この金型に流し込むだけで、一瞬で新しいチェックリストの型を自動で作ってくれます。
この2つが合わさることで、「元のデータの名前をそのまま使い回して、中身の型だけを自由自在に変形させる」という、プロの現場でも毎日使う強力なテクニックが完成します。
—
2. ステップ1:`keyof` 演算子で「キー」を型抜きしてみよう
まずは基本の `keyof`(キーオブ)から始めましょう。
名前の通り、「〜のキー(key of …)」という意味です。
実際のコードを見てみましょう。あなたのエディタにそのまま貼り付けて、動かして遊んでみてくださいね。
// 1. まずは元となる「スマートフォンのスペック」の型を定義します
type SmartPhone = {
brand: string; // ブランド名(例: “Apple”, “Google”)
price: number; // 価格(例: 120000)
is5G: boolean; // 5Gに対応しているか(true / false)
};
// 2. ここで keyof の登場です!
// SmartPhone型が持っている「キー(項目名)」だけを抜き出します
type PhoneKeys = keyof SmartPhone;
// これにより、PhoneKeys型は中身で以下のように解釈されます:
// type PhoneKeys = “brand” | “price” | “is5G”;
// (”brand” か “price” か “is5G” の3つの文字列しか受け付けない型になります)
// — 使ってみましょう! —
// 〇 正しい使い方:存在するキーを指定しているので、エラーになりません
const myKey1: PhoneKeys = “brand”;
const myKey2: PhoneKeys = “price”;
// ✕ 間違った使い方:SmartPhone型に存在しないキーを指定すると、TypeScriptが「そんな項目ないよ!」と優しく教えてくれます
// (※エディタ上で赤い波線が出ます。コメントを外して試してみてください)
// const myKey3: PhoneKeys = “color”;
どうでしょうか? `keyof` を使うと、オブジェクトの設計図(型)から、プロパティの名前だけを安全に抽出できるのが分かりますね。これだけでも、タイポ(打ち間違い)を防ぐための強力な味方になってくれます。
—
3. ステップ2:Mapped Typesで「型の量産」をしてみよう
次に、Mapped Types(マップドタイプス)です。
これは「マップ(対応付け)された型」という意味で、JavaScriptの配列で使う `map` メソッド(配列の要素を一つずつ加工して新しい配列を作るやつですね!)の型バージョンだと思ってください。
「あるリストの中身を、全部〇〇型に変換する」という処理を、型定義の中でやってのけます。
まずは、もっともシンプルな「全部 `boolean`(はい/いいえ)型に変えちゃう金型」を作ってみましょう。
// 変換の元になるキーのリスト(ユニオン型)を用意します
type StandardKeys = “power” | “wifi” | “bluetooth”;
// Mapped Types を使って、これらのキーをすべて「boolean型(true/false)」にした新しい型を作ります
type DeviceStatus = {
// [K in StandardKeys] は、「StandardKeysの中にあるキー(K)を、1つずつ取り出すよ」という意味です
[K in StandardKeys]: boolean;
};
// 実際にできあがった DeviceStatus 型は、裏側で以下のようになっています:
// type DeviceStatus = {
// power: boolean;
// wifi: boolean;
// bluetooth: boolean;
// }
// — 使ってみましょう! —
const myDevice: DeviceStatus = {
power: true,
wifi: false,
bluetooth: true,
// GPS: true // ✕ エラー!StandardKeysに「GPS」はないので追加できません
};
この `[K in KeyList]` という書き方が、最初はちょっと呪文のように見えてビックリしますよね。
でも、仕組みはとてもシンプルです。
- `in` は、「このリストの中から順番に取り出すよ」という合図。
- `K` は、今取り出しているキーの名前(一時的なあだ名。Keyの `K` です)。
「`StandardKeys` から1つずつ名前を取り出して `K` と名付け、その `K` の型をすべて `boolean` にします!」という、型専用の小さな `for` ループを回しているイメージです。
—
4. ステップ3:合体!`keyof` と Mapped Types を組み合わせる
さあ、いよいよ本番です!この2つの魔法を合体させてみましょう。
実務でよくあるリアルな場面を想像してください。
「ユーザーのプロフィールデータ」があるとします。普段はすべての項目がしっかり入力されていますが、「設定変更画面」では、ユーザーが変更したい項目だけを部分的に入力できるようにしたい(つまり、すべての項目を任意(オプショナル:あってもなくても良い)にしたい)というケースです。
手動でコピペして新しい型を作ってもいいのですが、もし将来プロフィールに「年齢(`age`)」や「住所(`address`)」が増えたら、両方の型を手作業で直さなければならず、いつか絶対に修正漏れやコピペミスが起きてしまいますよね。
そこで、`keyof` で元の型から自動でキーを抜き出し、Mapped Types で一気に「オプショナル(`?` 付き)」に変換する自動翻訳機を作ります。
// 1. 元になる「完成されたプロフィール」の型です
type UserProfile = {
name: string;
email: string;
avatarUrl: string;
};
// 2. keyof と Mapped Types を合体させた「型変換の魔法の金型(ジェネリック)」を作ります!
// 「T」には、どんな型でも入れることができます(今回はUserProfileが入ります)
type MakeOptional
// Tのキー(keyof T)を1つずつ取り出して「K」とし、
// そのキーの後ろに「?」をつけて、値の型は元の型「T[K]」のままにします
[K in keyof T]?: T[K];
};
// 3. 魔法を実行します!UserProfileを流し込んで、新しい型を作ります
type PartialUserProfile = MakeOptional
// これにより、できあがった PartialUserProfile 型は自動的にこうなります:
// type PartialUserProfile = {
// name?: string;
// email?: string;
// avatarUrl?: string;
// }
// (すべての項目に「?」がついて、あってもなくても良い状態になりました!)
// — 使ってみましょう! —
// ユーザーが「名前だけ変更したい」という時も、この型ならエラーになりません
const nameUpdateOnly: PartialUserProfile = {
name: “新しい名前” // email や avatarUrl がなくても怒られません!
};
いかがでしょうか!
手動で `name?: string` と書き直したわけではないのに、TypeScriptが元の `UserProfile` から自動で名前を読み取り、すべてに `?` をつけた新しい型を生成してくれました。
もし将来、`UserProfile` に `phoneNumber: string` が追加されても、この `MakeOptional` という金型は何も変更しなくて大丈夫です。自動的に電話番号もオプショナルにしてくれます。これが、「DRY(Don’t Repeat Yourself:同じことを繰り返さない)」という、プロの開発現場で最も大切にされている考え方です。
—
5. つまずきやすいポイントへの温かいフォロー
ここまで読んで、「うーん、頭ではなんとなく分かったけれど、自分でゼロからこのコードを書ける自信がないな…」と思っても、まったく心配いりません。本当に、大丈夫ですよ。
この `keyof` や Mapped Types は、TypeScriptの中でも「高度な型(Advanced Types)」と呼ばれる領域の入り口です。
現場のプロのエンジニアでも、これらを書くときは一発で書けずに「あれ? `in` の位置はここだっけ?」「`keyof` はどこに付けるんだっけ?」と、公式ドキュメントや過去の自分のコードを見返しながら書いています。
まずは、
- 「型からキー(プロパティ名)だけを型抜きできる `keyof` という便利グッズがあるんだな」
- 「型をループ処理して別の型に一括変換する Mapped Types という仕組みがあるんだな」
という存在を知っておくだけで、100点満点です!
いつか「似たような型を何個もコピペで作っていて、管理がめんどくさいな…」と思ったその時に、このブログを思い出して読み返してみてください。その時、パズルのピースがピタッとハマるような感動が待っているはずです。
まとめ
今回は、TypeScriptの強力な機能である `keyof` と Mapped Types について学びました。
- `keyof`:オブジェクトの型からキーだけを取り出して、安全な選択肢(ユニオン型)を作る。
- Mapped Types:キーのリストをループして、新しいオブジェクトの型を自動で量産する。
- 組み合わせる:元の型を壊さずに、一部のルール(必須をオプショナルにする、など)を自動で変換した新しい型を作れる。
TypeScriptは、あなたを縛るためのルールブックではなく、「未来のあなたやチームの仲間が、タイポやバグで夜遅くまで残業しなくて済むように守ってくれる盾」です。
焦らず、少しずつ仲良くなっていきましょう。あなたのフロントエンド開発の旅が、もっと楽しく、素晴らしいものになることを心から応援しています!

コメント