【入門編】 マップ型(Mapped Types)の基礎 – TypeScript実践ガイド

こんにちは!フロントエンド・アーキテクチャの世界へようこそ。
日々、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` とラップするだけで、元の `User` 型の構造を維持したまま、中のプロパティすべてを一発で読み取り専用に変えることができました。

ちなみに、TypeScriptには最初からこれができる組み込みの便利型として `Readonly` というものが用意されていますが、その内部もまさにこのマップ型で書かれているんです。仕組みが分かると、TypeScriptが魔法ではなく、ただの「よくできた道具」に見えてきませんか?

—

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ライフが、少しでも快適で楽しいものになりますように。それではまた、次の技術でお会いしましょう!

コメント

タイトルとURLをコピーしました