ジェネリクス: 型を抽象化

型安全で再利用可能なコード。any を使わない。

ジェネリクス
型をパラメータ化。
制約
extends で型を限定。
型推論
コンパイラが自動で型を決定。

ジェネリクス基礎

車のドリンクホルダー (Car Cup Holder)

ジェネリクスは「ドリンクホルダー」です。そこにはコーヒー(string)も、コーラ(number)も、お茶(object)も入ります。しかし、「今入っているのはコーヒーだ」と一度決まれば、車の揺れで中身がコーラに変わったりはしません。型を「後から指定」できる箱のようなものです。

型をハードコードするのではなく、変数(型パラメータ `T`)として扱います。これにより、一つの関数やクラスを様々な型で再利用でき、かつ `any` を使うのと違って「何の型が入っているか」を厳密に追跡できます。

Generic Functions
// ジェネリック関数
function identity<T>(value: T): T {
return value;
}
identity<string>("hello"); // 明示的
identity(42); // 推論される
// ジェネリック配列関数
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
// 複数の型パラメータ
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
Bad
// ❌ Bad: any で型安全性を失う
function identity(value: any): any {
return value;
}
Good
// ✅ Good: ジェネリックで型保持
function identity<T>(value: T): T {
return value;
}

パターン

Interface, Class, Constraints
// インターフェースのジェネリック
interface Box<T> {
value: T;
}
const numBox: Box<number> = { value: 42 };
// クラスのジェネリック
class Queue<T> {
private items: T[] = [];
enqueue(item: T) { this.items.push(item); }
dequeue(): T | undefined { return this.items.shift(); }
}
// 制約付きジェネリック
function getLength<T extends { length: number }>(item: T): number {
return item.length;
}
getLength("hello"); // OK
getLength([1, 2, 3]); // OK
// getLength(42); // Error!
// デフォルト型
type Container<T = string> = { value: T };
Tip: 迷ったら any ではなく unknown。さらに良いのはジェネリクス。

合格ライン

ジェネリック関数を書ける
制約付きジェネリクスを使える

演習課題

課題1: ジェネリック関数
ジェネリック関数を作成してください。
課題2: 制約付きジェネリクス
extends で制約付きジェネリクスを実装してください。