TypeScriptのMapped Typesとインターフェースの連携方法を徹底解説!初心者でも理解できる型変換の基本
生徒
「先生、TypeScriptで“Mapped Types(マップド型)”っていう言葉を見たんですが、これは何ですか?」
先生
「いいところに気づきましたね。Mapped Typesは、既にある型やインターフェースをもとに、新しい型を作るための仕組みなんです。」
生徒
「なるほど!それって、インターフェースとも一緒に使えるんですか?」
先生
「もちろんです!実は、インターフェースとMapped Typesを組み合わせることで、柔軟で使いやすい型を作れるようになりますよ。それでは、実際の例を見ながら学んでいきましょう!」
1. Mapped Types(マップド型)とは?
TypeScriptのMapped Types(マップド型)とは、既存の型やインターフェースのすべてのプロパティに、ある特定の変更をまとめて適用できる便利な仕組みです。英語の“map(マップ)”は「対応付ける」という意味があり、「ある型の各プロパティに対して新しい型を作る」というイメージです。
例えば、すべてのプロパティをオプション(省略可能)にしたり、すべてを読み取り専用(readonly)にしたりできます。
2. 基本的なMapped Typeの書き方
Mapped Typesは、以下のような構文で書きます。
type MyMappedType<T> = {
[P in keyof T]: T[P];
};
keyofは、指定した型のすべてのキー(プロパティ名)を取り出します。P in keyof Tは「Tのプロパティを1つずつ取り出して、新しい型を作る」という意味です。
実際にどんなふうに使えるかを、次の例で見てみましょう。
3. インターフェースとMapped Typesを組み合わせる
まず、基本となるインターフェースを作成します。
interface User {
name: string;
age: number;
email: string;
}
このUserインターフェースをもとに、すべてのプロパティをオプションにしたい場合、Mapped Typesを使うと簡単にできます。
type OptionalUser = {
[K in keyof User]?: User[K];
};
上の例では、?を追加することで、すべてのプロパティが省略可能になりました。つまり、OptionalUserは部分的なユーザー情報でも使えるようになります。
const user1: OptionalUser = { name: "Taro" };
(この場合、ageやemailを省略してもOKです)
4. TypeScriptの組み込みMapped Types
TypeScriptには、よく使うMapped Typesが最初から用意されています。代表的なものは次の通りです。
Partial<T>:すべてのプロパティをオプションにするReadonly<T>:すべてのプロパティを読み取り専用にするRequired<T>:すべてのプロパティを必須にするPick<T, K>:指定したプロパティだけを抜き出すRecord<K, T>:キーと値の型を指定して新しいオブジェクト型を作る
これらを使えば、自分でMapped Typesを作らなくても便利に型を操作できます。
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
これで、次のような使い方ができます。
const user2: PartialUser = { email: "test@example.com" }; // OK
const user3: ReadonlyUser = { name: "Hanako", age: 25, email: "hana@example.com" };
// user3.name = "Yuki"; // エラー:読み取り専用
5. 実用例:API更新処理にMapped Typesを使う
現実的な例として、「ユーザー情報の一部だけを更新するAPI」を考えてみましょう。通常、全ての項目を送らなくても、一部の情報だけを変更したい場合があります。
そのようなときにPartial<T>を使うと便利です。
function updateUser(id: number, data: Partial<User>) {
console.log("更新するユーザーID:", id);
console.log("更新データ:", data);
}
updateUser(1, { email: "new@example.com" });
更新するユーザーID: 1
更新データ: { email: "new@example.com" }
このように、Mapped Typesを使えば、柔軟にデータの更新や部分的な変更が可能になります。
6. カスタムMapped Typeを作って拡張する
自分でカスタムMapped Typeを作ることもできます。例えば、全てのプロパティを「文字列に変換する」型を作ってみましょう。
type Stringify<T> = {
[K in keyof T]: string;
};
type StringUser = Stringify<User>;
このStringUser型は、Userの各プロパティをすべて文字列に変換したものになります。
const user4: StringUser = {
name: "Alice",
age: "30",
email: "alice@example.com"
};
このようにMapped Typesを使えば、型の構造を保ちながら、値の型だけを自由に変換できます。
7. Mapped Typesとインターフェースを使いこなそう
Mapped Typesは、インターフェースと組み合わせることで、開発効率をぐっと上げることができます。特に、データ更新処理や型の再利用が多い大規模なアプリケーションでは、同じ構造を繰り返し定義する必要がなくなり、コードの保守性も向上します。
インターフェースで基本構造を定義し、Mapped Typesで柔軟に変化させる。これがTypeScriptの型システムの大きな魅力です。
まとめ
TypeScriptのMapped Typesとインターフェースを組み合わせて使うことで、型の再利用性や拡張性が大幅に向上し、複雑なデータ構造を扱う場面でも柔軟に対応できるようになります。この記事では、Mapped Typesの基本から、インターフェースとの連携、さらに実践的な活用方法まで段階的に学んできました。振り返ってみると、Mapped Typesは「型の変換をより簡単に、より一括で行う」ための強力な仕組みであり、日常的なアプリケーション設計でも役立つことが多いと感じられたのではないでしょうか。特に、既存の型を部分的に変更したいときや、API通信で部分更新を行いたいときなどは、その便利さを強く実感できます。
インターフェースで基本の型を定義し、そこにMapped Typesで応用的な制御を加えるという方法は、プログラムの見通しを良くし、プロパティの変更範囲を明確にする効果があります。たとえば、あるデータを全てオプションにしたいときはPartial<T>を使い、逆に全て必須にしたいときはRequired<T>を使えます。このように、必要なシーンに応じて型を自在に変形させられるのがMapped Typesの魅力です。
また、カスタムMapped Typeを作ることで、より自分の用途に合った型変換が可能になります。たとえば、すべてのプロパティの型を別の型に変換する仕組みを作れば、フォームデータの構造変換や入力値チェックなどにも応用できます。TypeScriptの型システムは、使えば使うほどその奥深さが見えてくるため、今回触れた概念を足がかりに、さらに複雑な型操作にも挑戦してみるとよいでしょう。
実践例:Mapped Typesを使った柔軟な型操作
具体的な応用例として、「プロパティをすべて読み取り専用にしたデータ型」と「プロパティをすべてオプションにしたデータ型」を同時に扱うケースを考えてみます。以下のサンプルでは、インターフェースで基本構造を定義し、Mapped Typesを使って派生型を作成しています。
interface Product {
title: string;
price: number;
description: string;
}
type ReadonlyProduct = Readonly<Product>;
type OptionalProduct = Partial<Product>;
const p1: ReadonlyProduct = {
title: "ノート",
price: 300,
description: "シンプルな無地ノート"
};
const p2: OptionalProduct = {
price: 150
};
// p1.title = "変更不可"; // エラー:ReadOnlyのため変更できない
このように、同じ元の型を基準にしながら、用途に応じて柔軟に変形させられるのがMapped Typesの強みです。特に大規模開発では、同じ構造を保ちながらも異なる制約を持った型を複数使うことがよくあるため、Mapped Typesは欠かせない存在となります。
もっと理解を深めるために
Mapped Typesは単体で理解するよりも、インターフェースやユニオン型、ジェネリクスなどと組み合わせて使うことで威力を発揮します。型の定義が柔軟であるほど、コードの再利用性は高まり、保守性も良くなります。今回学んだ「keyof」「Partial」「Readonly」などの仕組みは、TypeScriptの型操作の基礎となる部分なので、ぜひ繰り返し使いながら慣れていきましょう。
生徒
「先生、Mapped Typesって思ったより便利なんだなって分かりました!特にPartialとReadonlyがすごく使いやすいですね。」
先生
「その通りです。Mapped Typesは型を自由に変換できるので、柔軟に使うほどTypeScriptの魅力が見えてきますよ。」
生徒
「インターフェースをもとに必要な部分だけ変えられるのはとても便利ですね。大規模開発でも活躍しそうです。」
先生
「ええ、特にAPIの更新処理や部分的なデータ変更には欠かせない仕組みです。これからもMapped Typesをうまく使いこなしてくださいね。」