Tips & Tricks

Generics de TypeScript con Claude Code

Aprenda sobre generics de TypeScript usando Claude Code. Incluye consejos practicos y ejemplos de codigo.

ジェネリクスをClaude Codeで自在に操る

TypeScriptのジェネリクスは、再利用性の高い型安全なコードを書くための強力な仕組みです。Claude Codeはジェネリクスのパターンを深く理解しており、適切な型パラメータの設計から複雑な制約の記述まで的確にサポートしてくれます。

基本的なジェネリック関数の生成

まずはClaude Codeにシンプルなジェネリック関数を生成してもらいましょう。

> 配列から重複を除去するジェネリック関数を作成して。
> オブジェクト配列にも対応し、キーを指定できるようにして。
function uniqueBy<T, K extends keyof T>(array: T[], key?: K): T[] {
  if (!key) {
    return [...new Set(array)];
  }
  const seen = new Map<T[K], T>();
  for (const item of array) {
    const value = item[key];
    if (!seen.has(value)) {
      seen.set(value, item);
    }
  }
  return [...seen.values()];
}

// Usage example
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice (duplicate)" },
];

const unique = uniqueBy(users, "id");
// => [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

K extends keyof T という制約により、存在しないプロパティを指定するとコンパイル時にエラーになります。

条件型を使った高度なパターン

Claude Codeは条件型(Conditional Types)も的確に生成します。

> APIレスポンスの型を、成功時とエラー時で分岐するジェネリック型を作って。
> ステータスコードに応じて型が変わるようにして。
type ApiResult<T, E = Error> =
  | { status: "success"; data: T; error: never }
  | { status: "error"; data: never; error: E };

type ExtractData<R> = R extends { status: "success"; data: infer D } ? D : never;
type ExtractError<R> = R extends { status: "error"; error: infer E } ? E : never;

// レスポンスハンドラー
function handleResponse<T>(result: ApiResult<T>): T {
  if (result.status === "success") {
    return result.data; // 型が T に絞り込まれる
  }
  throw result.error;
}

// Usage example
interface User {
  id: string;
  name: string;
}

const result: ApiResult<User> = await fetchUser("123");
const user = handleResponse(result); // 型は User

ジェネリッククラスでリポジトリパターン

データアクセス層をジェネリクスで抽象化するパターンも、Claude Codeに任せると効率的です。

interface Entity {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

interface Repository<T extends Entity> {
  findById(id: string): Promise<T | null>;
  findMany(filter: Partial<T>): Promise<T[]>;
  create(data: Omit<T, "id" | "createdAt" | "updatedAt">): Promise<T>;
  update(id: string, data: Partial<Omit<T, "id">>): Promise<T>;
  delete(id: string): Promise<void>;
}

class PrismaRepository<T extends Entity> implements Repository<T> {
  constructor(private model: any) {}

  async findById(id: string): Promise<T | null> {
    return this.model.findUnique({ where: { id } });
  }

  async findMany(filter: Partial<T>): Promise<T[]> {
    return this.model.findMany({ where: filter });
  }

  async create(data: Omit<T, "id" | "createdAt" | "updatedAt">): Promise<T> {
    return this.model.create({ data });
  }

  async update(id: string, data: Partial<Omit<T, "id">>): Promise<T> {
    return this.model.update({ where: { id }, data });
  }

  async delete(id: string): Promise<void> {
    await this.model.delete({ where: { id } });
  }
}

ジェネリクスの制約を活用したビルダーパターン

型パラメータの制約を組み合わせた型安全なビルダーも生成できます。

type QueryBuilder<T extends Record<string, unknown>> = {
  where<K extends keyof T>(key: K, value: T[K]): QueryBuilder<T>;
  orderBy<K extends keyof T>(key: K, direction: "asc" | "desc"): QueryBuilder<T>;
  select<K extends keyof T>(...keys: K[]): QueryBuilder<Pick<T, K>>;
  execute(): Promise<T[]>;
};

function createQuery<T extends Record<string, unknown>>(
  table: string
): QueryBuilder<T> {
  const conditions: string[] = [];
  const orders: string[] = [];
  let selectedFields: string[] = [];

  const builder: QueryBuilder<T> = {
    where(key, value) {
      conditions.push(`${String(key)} = '${value}'`);
      return builder as any;
    },
    orderBy(key, direction) {
      orders.push(`${String(key)} ${direction}`);
      return builder;
    },
    select(...keys) {
      selectedFields = keys.map(String);
      return builder as any;
    },
    async execute() {
      // 実際のクエリ実行ロジック
      return [];
    },
  };

  return builder;
}

Claude Codeへの効果的なプロンプト

ジェネリクスをClaude Codeに依頼する際のコツをまとめます。

  1. 具体的な使用例を示す - 「このような入出力になる関数」と伝える
  2. 制約を明示する - 「TはRecord型に限定」など型制約を指定する
  3. ユースケースを複数提示する - 多様な使い方を示すと精度が上がる
> ジェネリック型 DeepPartial<T> を作って。
> ネストしたオブジェクトのすべてのプロパティをオプショナルにする。
> 配列の要素もDeepPartialにして。
type DeepPartial<T> = T extends (infer U)[]
  ? DeepPartial<U>[]
  : T extends object
    ? { [K in keyof T]?: DeepPartial<T[K]> }
    : T;

Summary

Claude Codeはジェネリクスの生成と最適化に非常に強力です。型パラメータの設計、制約の付与、条件型の活用など、複雑なパターンもプロンプト一つで実装できます。

TypeScriptの型活用をさらに深めたい方はTypeScript開発を加速する実践Tipsを、ユーティリティ型の応用はユーティリティ型活用ガイドを参照してください。また、TypeScript公式ハンドブックのジェネリクスも併せて確認すると理解が深まります。

#Claude Code #TypeScript #generics #type safety #design patterns