TypeScript

ジェネリクス

TypeScript

型パラメーターと制約

ジェネリクスの基本

型パラメーターで再利用可能な型を定義

generics.ts typescript
// ジェネリック関数
function identity<T>(value: T): T {
    return value;
}
identity<string>('hello'); // 'hello'
identity(42);              // 型推論: number

// 配列の最初の要素
function first<T>(arr: T[]): T | undefined {
    return arr[0];
}
first([1, 2, 3]);     // number | undefined
first(['a', 'b']);    // string | undefined

// ジェネリックインターフェース
interface Repository<T> {
    findById(id: number): Promise<T>;
    findAll(): Promise<T[]>;
    save(entity: T): Promise<T>;
    delete(id: number): Promise<void>;
}

// ジェネリッククラス
class Stack<T> {
    private items: T[] = [];
    push(item: T):  void    { this.items.push(item); }
    pop():          T | undefined { return this.items.pop(); }
    peek():         T | undefined { return this.items.at(-1); }
    isEmpty():      boolean  { return this.items.length === 0; }
}

型制約

extends で型パラメーターを制限する

constraints.ts typescript
// extends による制約
function getLength<T extends { length: number }>(val: T): number {
    return val.length;
}
getLength('hello');   // 5
getLength([1, 2, 3]); // 3
// getLength(42);     // ❌ エラー: numberはlengthを持たない

// keyof 制約
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}
const user = { name: 'Alice', age: 30 };
getProperty(user, 'name'); // string
getProperty(user, 'age');  // number
// getProperty(user, 'email'); // ❌ エラー

// 複数の制約
function merge<T extends object, U extends object>(a: T, b: U): T & U {
    return { ...a, ...b };
}

// デフォルト型パラメーター
interface ApiResponse<T = unknown> {
    data:    T;
    status:  number;
    message: string;
}
type UserResponse = ApiResponse<User>; // data は User 型
type RawResponse  = ApiResponse;       // data は unknown 型

実用ジェネリックパターン

ReturnType、Parameters、infer など

patterns.ts typescript
// infer — 型の推論
type ReturnType<T extends (...args: any) => any> =
    T extends (...args: any) => infer R ? R : never;

type Unpacked<T> =
    T extends Array<infer Item>        ? Item :
    T extends Promise<infer Resolved>  ? Resolved :
    T;

type A = Unpacked<string[]>;          // string
type B = Unpacked<Promise<number>>;   // number

// 非同期関数の戻り値型
async function fetchUser(): Promise<User> { /* ... */ return user; }
type FetchResult = Awaited<ReturnType<typeof fetchUser>>; // User

// 条件型 + ジェネリクス
type NonNullable<T> = T extends null | undefined ? never : T;
type Flatten<T> = T extends Array<infer Item> ? Item : T;

// Mapped Type + ジェネリクス
type Nullable<T>  = { [K in keyof T]: T[K] | null };
type Optional<T>  = { [K in keyof T]?: T[K] };
type Stringify<T> = { [K in keyof T]: string };