インターフェースとジェネリクス
より複雑で再利用可能な型定義を学びましょう。
インターフェース (Interface)
インターフェースは、オブジェクトの「形」を定義する機能です。同じ形のオブジェクトを何度も使う場合に便利です。
基本的な使い方
// インターフェースの定義 interface User { name: string; age: number; email: string; } // インターフェースを使う const user1: User = { name: "太郎", age: 25, email: "taro@example.com" }; const user2: User = { name: "花子", age: 23, email: "hanako@example.com" };
メリット:
- 型定義を再利用できる
- コードが読みやすくなる
- 同じ形のオブジェクトを複数作る時に便利
オプショナルプロパティ
必須ではないプロパティには ? をつけます。
interface User { name: string; age: number; email: string; phone?: string; // phoneは省略可能 } // phoneがなくてもOK const user1: User = { name: "太郎", age: 25, email: "taro@example.com" }; // phoneがあってもOK const user2: User = { name: "花子", age: 23, email: "hanako@example.com", phone: "090-1234-5678" };
読み取り専用プロパティ
変更してほしくないプロパティには readonly をつけます。
interface User { readonly id: number; // 読み取り専用 name: string; age: number; } const user: User = { id: 1, name: "太郎", age: 25 }; // user.id = 2; // エラー!idは読み取り専用なので変更できない user.name = "次郎"; // OK
ジェネリクス (Generics)
ジェネリクスは、「型をパラメータとして受け取る」機能です。柔軟で再利用可能なコードを書けます。
なぜジェネリクスが必要?
例えば、配列の最初の要素を返す関数を考えてみましょう。
❌ ジェネリクスを使わない場合
// 数値配列用 function firstNumber(arr: number[]): number { return arr[0]; } // 文字列配列用 function firstString(arr: string[]): string { return arr[0]; } // 型ごとに関数を作る必要がある... 😰
✅ ジェネリクスを使う場合
function first<T>(arr: T[]): T { return arr[0]; } // どんな型でも使える! const num = first([1, 2, 3]); // numはnumber型 const str = first(["a", "b", "c"]); // strはstring型
ジェネリクスの書き方
<T> の部分が「型パラメータ」です。Tは慣習的によく使われますが、任意の名前でOKです。
// 基本形 function 関数名<T>(引数: T): T { return 引数; }
実用的な例:データのラッパー
interface ApiResponse<T> { data: T; status: number; message: string; } // ユーザーデータのレスポンス const userResponse: ApiResponse<User> = { data: { name: "太郎", age: 25, email: "taro@example.com" }, status: 200, message: "Success" }; // 商品データのレスポンス interface Product { id: number; name: string; price: number; } const productResponse: ApiResponse<Product> = { data: { id: 1, name: "ノートPC", price: 120000 }, status: 200, message: "Success" };
複数の型パラメータ
function pair<T, U>(first: T, second: U): [T, U] { return [first, second]; } const result1 = pair("age", 25); // [string, number] const result2 = pair(true, "success"); // [boolean, string]
インターフェース vs 型エイリアス
TypeScriptには、インターフェース以外に「型エイリアス」という機能もあります。
// インターフェース interface User { name: string; age: number; } // 型エイリアス type User = { name: string; age: number; };
どちらを使うべき?
- オブジェクトの型定義:インターフェースを推奨
- ユニオン型やプリミティブ型:型エイリアスを使う
// こういう場合は型エイリアスを使う type Status = "pending" | "success" | "error"; type ID = string | number;
実践例:Todoアプリの型定義
interface Todo { id: number; title: string; completed: boolean; createdAt: Date; tags?: string[]; } interface TodoList<T> { items: T[]; add: (item: T) => void; remove: (id: number) => void; } // 使用例 const myTodos: TodoList<Todo> = { items: [ { id: 1, title: "TypeScriptを学ぶ", completed: false, createdAt: new Date(), tags: ["学習", "プログラミング"] } ], add: (todo) => { myTodos.items.push(todo); }, remove: (id) => { myTodos.items = myTodos.items.filter(item => item.id !== id); } };
まとめ
インターフェース
- オブジェクトの「形」を定義する
?でオプショナルプロパティreadonlyで読み取り専用プロパティ- 再利用可能で読みやすいコードを書ける
ジェネリクス
- 型をパラメータとして受け取る
<T>のように書く- 柔軟で再利用可能な関数やインターフェースを作れる
- 型安全性を保ちながら汎用的なコードが書ける
次はReactの基礎を学びましょう!