こんにちは!フロントエンド・アーキテクチャの世界へようこそ。
日々、TypeScriptと格闘しながら「もっとスマートに型を書けないものか……」と頭を悩ませているあなたへ、このブログをお届けします。
今回は、TypeScriptの中級への扉を叩くための最重要テクニックの一つ、「マップ型(Mapped Types)」の基本についてお話しします。
「名前だけ聞いたことがあるけれど、なんだか難しそう……」
「公式ドキュメントを見たけれど、記号の暗号にしか見えない……」
大丈夫ですよ、安心してください。今日ここで、あなたの頭の中にある「?」を「なるほど!」に変えてみせます。難しい専門用語はいったん脇に置いて、身近な例えからゆっくり紐解いていきましょう。
—
1. マップ型ってなぁに?(お片付けボックスの例え)
TypeScriptを書いていると、こんなシチュエーションに出会ったことはありませんか?
> 「あー、さっき作ったこのユーザー情報の型、全部のプロパティを『読み取り専用(readonly)』にしたいな……」
> 「または、全部の項目を『フォームに入力中だから、とりあえず文字列(string)か、あるいは未入力(null)かも』っていう状態に変えたいな……」
普通のプログラミングなら、同じような型をもう一度コピペして作り直したくなりますよね。でも、ちょっと待って!それだと元の型が変わったときに、何箇所も直さなきゃいけない「保守地獄」の完成です。DRY(Don’t Repeat Yourself)原則の精神にも反しますよね。
ここで登場するのがマップ型です。
イメージとしては、「お気に入りの定食メニュー(既存の型)を差し込むと、自動的にすべての小鉢に『テイクアウト用シール(修飾子)』をペタペタと貼ってくれる魔法のベルトコンベア」のようなもの。
既存の型を一つひとつ手作業で書き直すのではなく、型を「反復処理(ループ)」して、効率よく新しい型を生み出す仕組みがマップ型なのです。
—
2. まずは基本の形を見てみよう
百聞は一見にしかず。まずはコードの「形」に慣れちゃいましょう。
マップ型の基本構文は、JavaScriptの `for…in` ループや、Arrayの `map` メソッドにちょっと似ています。
// 基本の構文のイメージ
type NewType
[K in keyof T]: 変換ルール;
};
「うわ、出た!意味不明なアルファベットの羅列だ!」って思いました?大丈夫、分解して優しく解説しますね。
1. `keyof T` : 「型 `T` が持っているすべてのプロパティ名(キー)のリスト」を取り出すという意味です。
2. `K in …` : 「そのリストの中から、プロパティ名を1つずつ `K` という変数に代入して取り出してね(ループしてね)」という意味です。
3. `[K in keyof T]` : 取り出したプロパティ名をそのまま新しい型のキーとして使いますよ、という宣言です。
これだけです。要するに、「元の型が持っているプロパティを全部なぞって、新しい型を組み立てるぜ!」と言っているだけなんですね。
—
3. 実践!すべてを「読み取り専用(readonly)」にする魔法
では、実務で本当によく使う例を見てみましょう。
あるユーザー情報があります。このままでは自由に書き換えができてしまいますが、「特定の画面では、絶対にデータを書き換えられたくない(イミュータブルにしたい)」という要件があるとします。
ここでマップ型の登場です。
// 1. 元となる普通のユーザー型
type User = {
id: number;
name: string;
email: string;
};
// 2. マップ型を使って、すべてのプロパティを readonly(読み取り専用)にする型を作る
type ReadonlyUser
readonly [K in keyof T]: T[K];
};
// 3. 実際に使ってみる
type StrictUser = ReadonlyUser
const user: StrictUser = {
id: 1,
name: “山田太郎”,
email: “yamada@example.com”
};
// 【エラーになる例】
// user.name = “鈴木次郎”;
// ❌ 怒られます!「Cannot assign to ‘name’ because it is a read-only property.」
すごいですね! `ReadonlyUser
ちなみに、TypeScriptには最初からこれができる組み込みの便利型として `Readonly
—
4. プロパティを「オプショナル(任意)」に変えてみる
もう一つのよくあるユースケースを見てみましょう。
例えば、プロフィール画面の「編集機能」です。ユーザーは名前だけを変えたいかもしれないし、メールアドレスだけを変えたいかもしれない。つまり、すべての項目が必須ではなく、「送られてくるかもしれないし、送られてこないかもしれない(オプショナル)」状態にしたいわけです。
そんなときは、プロパティ名の後ろに `?` をつけます。
// 元の型
type UserProfile = {
username: string;
bio: string;
age: number;
};
// すべてのプロパティを「あってもなくてもいい(オプショナル)」にするマップ型
type PartialProfile
[K in keyof T]?: T[K]; // ← ここに「?」をつける!
};
// 適用してみる
type EditableProfile = PartialProfile
// これで、一部だけの更新データも型エラーなく受け付けられるようになります!
const updateData: EditableProfile = {
bio: “TypeScriptが好きです!” // usernameやageがなくても怒られない!
};
これもまた、TypeScript公式が用意してくれている `Partial
—
5. つまずきやすいポイントと、温かいアドバイス
初心者の頃、私もマップ型を勉強していてよく頭がこんがらがりました。ここで、初心者がついハマりがちな罠と、その抜け出し方をお伝えしておきます。
つまずきポイント:エラーメッセージが長すぎて読む気がしない
マップ型を書き間違えると、TypeScriptは親切(だけど難解)な長いエラーメッセージを吐き出します。
そんなときは、一度マップ型を使うのをやめて、ベタ書き(普通のオブジェクト型)でどう書きたいのかを想像するのが近道です。
「あ、本当はこのプロパティをこうしたいんだっけ」とゴールが見えれば、マップ型の構文エラーも怖くありません。焦らず、小さなサンプルコードでコンパイル結果を確認しながら進めましょう。
—
まとめ
今回は、マップ型の基礎についてお話ししました。
- マップ型とは、既存の型を「ループ」して、新しい型を効率よく生み出す仕組み。
- `[K in keyof T]` というお決まりの構文でプロパティを走査する。
- `readonly` や `?` などの修飾子を組み合わせることで、既存の型を自由自在にカスタマイズできる。
最初は少し呪文のように見えるかもしれませんが、慣れてくると「これなしではTypeScriptが書けない!」というくらい強力な相棒になります。
現場のコードを美しく、そして堅牢にするために、ぜひ今日のコードを手元のエディタに貼り付けて、色々いじって遊んでみてくださいね。
あなたのTypeScriptライフが、少しでも快適で楽しいものになりますように。それではまた、次の技術でお会いしましょう!

コメント