TypeScriptのMapped Typesとは?基本構文と型の再構成例を解説
生徒
「TypeScriptで、既存の型を元にして新しい型を自動的に作る便利な方法ってありますか?」
先生
「Mapped Types(マップドタイプ)という機能を使うと、型を効率よく再構成して作成できますよ。」
生徒
「具体的にはどのような仕組みで、何が嬉しいんでしょうか?」
先生
「それでは、型の変換や再定義を楽にする基本的な使い方から順番に見ていきましょう!」
1. Mapped Typesとは?
TypeScriptのMapped Typesとは、既存の型から新しい型を生成する仕組みのことです。プログラミングにおいて、似たような構造を持つ型を何度も手動で書くのは大変です。例えば、すべての項目が変更可能(読み書き可能)な型があるとして、それと全く同じ構造で、全ての項目を読み取り専用(変更不可)にしたい場合、Mapped Typesを使うと自動的に変換できます。型定義を自動化することで、コードの重複を減らし、将来的な仕様変更にも強いプログラムが書けるようになります。
2. 基本構文のルール
Mapped Typesの基本的な書き方は、JavaScriptの配列を扱うmap関数とよく似ています。角括弧[]の中にinというキーワードを使って、既存の型のプロパティ名を一つずつ取り出して処理します。これにより、型安全性を保ちながら柔軟に型を変更できるのです。型推論という、TypeScriptが文脈から自動的に型を判断してくれる機能とも相性が良く、手作業によるミスを大きく減らすことができます。
type Original = {
name: string;
age: number;
};
type ReadOnlyCopy = {
readonly [P in keyof Original]: Original[P];
};
3. 型の再構成例:すべてをオプショナルにする
Mapped Typesの活用例として、すべてのプロパティをオプショナル(設定してもしなくても良い状態)にする方法を紹介します。プロパティ名の後ろに?を付けるだけで、簡単に変換が可能です。これにより、入力フォームなどで一部の項目だけを入力するような処理を、既存の型を使い回しながら安全に実装できます。オプショナルとは、値が存在しなくてもエラーにならない状態を指し、柔軟なデータ設計には欠かせない知識です。
type User = {
id: number;
name: string;
email: string;
};
type PartialUser = {
[P in keyof User]?: User[P];
};
4. 既存の型を変換するメリット
Mapped Typesを使用する最大のメリットは、プログラムの修正箇所を減らせることです。もし元の型に新しい項目が追加されても、Mapped Typesを使っていれば自動的に新しい型にも項目が反映されます。メンテナンス性(後からプログラムを修正しやすさ)が劇的に向上します。プログラミングの世界では、一度書いたコードをいかに使い回すかが重要であり、この手法はその極意といえます。
5. 修飾子の追加と削除
プロパティに対してreadonlyや?といった修飾子を追加するだけでなく、削除することも可能です。削除したい場合は、-readonlyや-?のようにマイナス記号を付けます。例えば、全て読み取り専用になってしまっている型から、無理やり変更可能な状態に戻したい場合などに役立ちます。修飾子とは、変数やプロパティの性質(書き換えができるか等)を指定する記号のようなものだと考えてください。
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
6. 複雑な型を扱うためのコツ
Mapped Typesをマスターするには、ジェネリクス(Generics)との組み合わせが重要です。ジェネリクスを使えば、特定の型だけでなく、どんな型が来ても変換できるような汎用的なルールを作れます。ジェネリクスとは、型を変数のように扱い、あとから具体的な型を指定できる仕組みです。最初は難しく感じるかもしれませんが、この技術を習得することで、よりプロらしい、再利用性の高いTypeScriptコードを書けるようになります。
type Stringify<T> = {
[P in keyof T]: string;
};
type UserInfo = { id: number; active: boolean };
type StringifiedUser = Stringify<UserInfo>;