【入門編】 Mapped Typesの基本構文と再利用 – TypeScript実践ガイド

こんにちは!TypeScriptの世界へようこそ。チーフアーキテクトの私です。

日々のフロントエンド開発、本当にお疲れ様です。「型定義ってなんだか難しそう……」「`any`で逃げちゃいたい!」そんな風に心が折れそうになっていませんか?
大丈夫です、安心してください。最初は誰もが同じ壁にぶつかります。

今回は、TypeScriptの中級者へステップアップするための強力な武器、「Mapped Types(マップド・タイプス)」についてお話しします。

なんだか呪文のような名前ですが、怖がる必要はありません。身近な例えを交えながら、一緒にフムフムと紐解いていきましょう!

—

1. Mapped Typesってなに?(お菓子の型抜きに例えてみよう)

突然ですが、クッキー作りを想像してみてください。
星型のクッキー生地があって、「全部のクッキーを、ちょっと大きめの星型に変えたい!」あるいは「全部のクッキーに『焦げ防止のアルミホイル(readonly)』を一枚ずつかぶせたい!」と思ったとします。

ひとつひとつ手作業で直していくのは、気の遠くなる作業ですよね。

TypeScriptの Mapped Types は、まさにこの「一括変換の型抜きスタンプ」です。
「あるオブジェクトの型をベースにして、ルールに従い、プロパティをごっそり一括で作り変えちゃう」という、超便利でちょっとカッコいい仕組みなんですよ。

基本の合言葉 `[K in keyof T]`

Mapped Typesの基本構文は、いつもこの形から始まります。

type NewType = {
[K in keyof T]: 変換ルール;
};

「うわ、なんか難しそう……」と思いましたか? 独り言だと思って声に出して読んでみてください。

  • `keyof T` :「Tというお弁当箱に入っているおかずの名前(キー)を全部リストアップして!」
  • `K in` :「そのリストアップしたおかずたちを、一つずつ `K` という変数に入れてループするよ!」

そう、これってJavaScriptの `for…in` ループや `map()` メソッドとやっていることは全く同じなんです。プログラミングの基本知見があれば、実はすんなり頭に入ってくるはずです。

—

2. 実践!オフィスのプロフィール型を「読取専用(readonly)」に変えてみよう

では、実際にエディタを叩く気持ちでコードを見てみましょう。
いま、Webサイトのユーザープロフィールを表す型があったとします。

// ユーザーのプロフィール
interface UserProfile {
name: string;
age: number;
}

このままだと、アプリのどこかでうっかり `profile.age = 20` のように書き換えてしまうバグが起きるかもしれません。
「このオブジェクトは、絶対に書き換えさせない!見るだけ(readonly)にするんだ!」という強い意志をTypeScriptに伝えたいとき、Mapped Typesの出番です。

// すべてのプロパティを「読み取り専用」にするMapped Types
type ReadonlyUserProfile = {
readonly [K in keyof T]: T[K];
};

// 実際に使ってみる
const safeUser: ReadonlyUserProfile = {
name: “田中さん”,
age: 28,
};

// エラー! readonlyなので書き換えようとすると怒られます
// safeUser.age = 30;

`readonly` を `[K in keyof T]` の頭につけるだけで、中のプロパティ全部が「触っちゃダメ!」の安全な箱に生まれ変わりました。すごいですよね。

—

3. 修飾子を自由自在に操る(付与と削除)

Mapped Typesの面白いところは、プロパティを「追加する」だけではなく、「消す(剥ぎ取る)」こともできる点です。

ここで登場するのが、TypeScriptのちょっとユニークな記号、`+` と `-` です。

  • `+` をつける(または何もつけない):修飾子を追加する
  • `-` をつける:修飾子を剥ぎ取る(削除する)

「任意(オプショナル)」を強制的に必須にする

例えば、フォームの入力データ型などを想像してください。最初は「未入力でもOK(`?`がついている)」だったものが、バリデーションを通った後には「絶対に値が入っている(必須)」になっていてほしい瞬間があります。

そんなときは `-?`(マイナス・オプショナル)の出番です!

// 最初は入力項目のいくつかが「なくてもいい(?)」状態
interface FormInput {
email?: string;
password?: string;
}

// 「?」を剥ぎ取って、すべて必須(Required)にするMapped Types
type ForceRequired = {
-readonly [K in keyof T]-?: T[K]; // readonlyも一緒に剥ぎ取る例
};

type ValidatedForm = ForceRequired;

// 使うときは、両方必ず書かないとTypeScriptに怒られます!
const data: ValidatedForm = {
email: “test@example.com”,
password: “super-secure-password”,
};

`-?` と書くことで、「オプショナル(?)を剥ぎ取る!」という命令になります。初めて見たときは「なんだこのマイナス記号は!」と驚いたものですが、仕組みが分かると非常に直感的でスマートですね。

—

4. 現場で役立つ!TypeScript標準の便利ユーティリティたち

ここまでMapped Typesの基本構造を解説してきましたが、実はTypeScriptには、先ほど紹介したようなよく使うパターンがあらかじめ標準で用意されています。

実務の現場では、わざわざ自分で書かなくても、以下のビルトイン(組み込み)型をそのまま使うことが多いです。

| 組み込み型 | やっていること(Mapped Typesの応用) | 身近な例え |
| :— | :— | :— |
| `Partial` | すべてのプロパティをオプショナル(`?`)にする | 「とりあえず何でも後から追加できる下書き状態にする」 |
| `Required` | すべてのプロパティを必須(`?`を剥ぐ)にする | 「空欄を許さない、ガチガチの清書用紙にする」 |
| `Readonly` | すべてのプロパティを読み取り専用にする | 「ラミネート加工して、ペンで書き込めなくする」 |

「型定義をちょっとだけ形を変えて使い回したいな」と思ったときは、これらのユーティリティ型が使えないかまず思い出してみてください。大抵のことはこれらで解決できてしまいます。

—

おわりに:焦らず、一歩ずつ自分のものに

Mapped Typesの基本、いかがでしたでしょうか?

最初は `[K in keyof T]` という見慣れない記号の並びに圧倒されてしまうかもしれませんが、「オブジェクトのキーをループして、一つひとつにルールを適用する型スタンプなんだな」というイメージさえ持てれば大丈夫です。

実務でコードを書くとき、「あ、ここ同じような型定義を何回も書いちゃってるな」「スマートにまとめられないかな」と思った瞬間が、Mapped Typesを導入する絶好のチャンスです。

焦らず、まずは手元のエディタでちまちまとコードをいじりながら、エラーと仲良くなりつつ楽しんでみてくださいね。あなたのTypeScriptライフが、少しでも快適で楽しいものになりますように!

コメント

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