mcpField
T
typescript-advanced

typescript-advanced

github.com/rohitg00開発2.8k193 回インストール25 週間前に更新
リポジトリ

typescript-advanced について

高度な型システムのパターンを扱うTypeScript開発者向けリファレンスです。ジェネリクス、条件型やマップ型、ユニオン、型ガード、ユーティリティ型の例と注意点を紹介します。

主な機能

  1. 01制約付きジェネリクス
  2. 02条件型とマップ型
  3. 03判別可能ユニオン
  4. 04型ガード
  5. 05ユーティリティ型の組み合わせ
  6. 06型安全性チェックリスト

活用シーン

  1. 01再利用可能なジェネリック関数の型付け
  2. 02結果や状態のバリエーションのモデル化
  3. 03実行時の不確かな値の検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rohitg00/awesome-claude-code-toolkit --skill typescript-advanced

インストール後、Claude Code で/typescript-advancedを入力して読み込まれたか確認します。

詳細

TypeScript の高度な機能

制約付きジェネリクス

interface HasId {
  id: string;
}

function findById<T extends HasId>(items: T[], id: string): T | undefined {
  return items.find(item => item.id === id);
}

function groupBy<T, K extends string | number>(
  items: T[],
  keyFn: (item: T) => K
): Record<K, T[]> {
  return items.reduce((acc, item) => {
    const key = keyFn(item);
    (acc[key] ??= []).push(item);
    return acc;
  }, {} as Record<K, T[]>);
}

type ApiResponse<T> = {
  data: T;
  meta: { page: number; total: number };
};

async function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
  const res = await fetch(url);
  return res.json();
}

条件型

type IsString<T> = T extends string ? true : false;

type Flatten<T> = T extends Array<infer U> ? U : T;

type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;

type FunctionReturn<T> = T extends (...args: any[]) => infer R ? R : never;

type ExtractRouteParams<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractRouteParams<Rest>
    : T extends `${string}:${infer Param}`
      ? Param
      : never;

type Params = ExtractRouteParams<"/users/:userId/posts/:postId">;

マップ型

type Readonly<T> = { readonly [K in keyof T]: T[K] };

type Optional<T> = { [K in keyof T]?: T[K] };

type Nullable<T> = { [K in keyof T]: T[K] | null };

type PickByType<T, V> = {
  [K in keyof T as T[K] extends V ? K : never]: T[K];
};

interface User {
  id: string;
  name: string;
  age: number;
  active: boolean;
}

type StringFields = PickByType<User, string>;

type EventMap<T> = {
  [K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void;
};

type UserEvents = EventMap<{ login: User; logout: string }>;

判別可能なユニオン

type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return { ok: false, error: "Division by zero" };
  return { ok: true, value: a / b };
}

const result = divide(10, 2);
if (result.ok) {
  console.log(result.value);
} else {
  console.error(result.error);
}

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "rect": return shape.width * shape.height;
    case "triangle": return 0.5 * shape.base * shape.height;
  }
}

型ガード

function isNonNull<T>(value: T | null | undefined): value is T {
  return value != null;
}

function hasProperty<K extends string>(
  obj: unknown,
  key: K
): obj is Record<K, unknown> {
  return typeof obj === "object" && obj !== null && key in obj;
}

const values = [1, null, 2, undefined, 3].filter(isNonNull);

function assertNever(value: never): never {
  throw new Error(`Unexpected value: ${value}`);
}

ユーティリティ型の組み合わせ

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

type StrictOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
  { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];

type UpdateUserInput = RequireAtLeastOne<{ name: string; email: string; age: number }>;

アンチパターン

  • 型が不確かな値に対して、unknown ではなく any を使用する
  • 型ガードのほうが安全な場合に、型アサーション(as)を使用する
  • 可読性を損なう、複雑すぎるジェネリックシグネチャ
  • 状態マシンや結果の型に判別可能なユニオンを使用しない
  • 文字列リテラルのユニオンで十分な場合に enum を使用する
  • tsconfig の strictNullChecks を無視する

チェックリスト

  • tsconfig で strict: true が有効になっている
  • 外部データには any ではなく unknown を使用している
  • 型ガードで実行時の型を安全に検証している
  • 判別可能なユニオンで状態をモデル化し、網羅的な switch を使用している
  • ジェネリック制約(extends)で誤用を防いでいる
  • マップ型を使用して、元の型から関連する型を導出している
  • 手動で型を再定義するより、ユーティリティ型(Pick、Omit、Partial)を優先している
  • 文字列パターンの制約にテンプレートリテラル型を使用している

コメント