mcpField
T
typescript-best-practices

typescript-best-practices

github.com/alleneubank開発503.4k 回インストール18 週間前に更新
リポジトリ

typescript-best-practices について

コーディングエージェント向けの TypeScript スキルで、型を起点とする設計、実行時検証、言語固有の慣用パターンを扱います。React のファイルでは `react-best-practices` との併用を推奨します。

主な機能

  1. 01型を起点とするモデリング
  2. 02判別可能なユニオンとブランド型
  3. 03Zod による実行時検証
  4. 04網羅性を確認する switch
  5. 05任意の type-fest ユーティリティ

活用シーン

  1. 01不正な組み合わせを排除する状態設計
  2. 02実行時入力の検証と正規化
  3. 03スキーマと推論型の整合性維持

インストール

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

$npx skills add https://github.com/alleneubank/claude-code --skill typescript-best-practices

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

詳細

TypeScript のベストプラクティス

CLAUDE.md の型優先、関数型、エラー処理のパターンに従います。このスキルでは、言語固有のイディオムのみを扱います。

React のベストプラクティスと併用する

React コンポーネント(.tsx、.jsx ファイル、または @react インポート)を扱う場合は、このスキルと一緒に必ず react-best-practices を読み込んでください。このスキルでは TypeScript の基本を扱い、エフェクト、フック、ref、コンポーネント設計といった React 固有のパターンは専用の React スキルで扱います。

不正な状態を表現できないようにする

コンパイル時に無効な状態を防げるよう、型システムを活用します。

相互排他的な状態には判別可能なユニオンを使用します:

// Good: only valid combinations possible
type RequestState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error };

// Bad: allows invalid combinations like { loading: true, error: Error }
type RequestState<T> = {
  loading: boolean;
  data?: T;
  error?: Error;
};

ドメインプリミティブにはブランド型を使用します:

type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };

// Compiler prevents passing OrderId where UserId expected
function getUser(id: UserId): Promise<User> { /* ... */ }

リテラルユニオンには const アサーションを使用します:

const ROLES = ['admin', 'user', 'guest'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'

// Array and type stay in sync automatically
function isValidRole(role: string): role is Role {
  return ROLES.includes(role as Role);
}

never チェックを使った網羅的な switch 文:

type Status = "active" | "inactive";

function processStatus(status: Status): string {
  switch (status) {
    case "active":
      return "processing";
    case "inactive":
      return "skipped";
    default: {
      const _exhaustive: never = status;
      throw new Error(`unhandled status: ${_exhaustive}`);
    }
  }
}

Zod による実行時検証

  • スキーマを唯一の信頼できる情報源として定義し、z.infer<> で TypeScript の型を推論します。型とスキーマの重複定義は避けてください。
  • 失敗が想定されるユーザー入力には safeParse を使用し、不正なデータがバグとなる信頼境界では parse を使用します。
  • .extend()、.pick()、.omit()、.merge() でスキーマを合成し、DRY な定義にします。
  • .transform() を使って、パース時にデータを正規化します(文字列のトリミング、日付のパースなど)。
import { z } from "zod";

const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string().min(1),
  createdAt: z.string().transform((s) => new Date(s)),
});

type User = z.infer<typeof UserSchema>;

// Strict parsing at trust boundaries — throws if API contract violated
export async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    throw new Error(`fetch user ${id} failed: ${response.status}`);
  }
  return UserSchema.parse(await response.json());
}

// Caller handles both success and error from user input
const result = UserSchema.safeParse(formData);
if (!result.success) {
  setErrors(result.error.flatten().fieldErrors);
  return;
}

任意:type-fest

TypeScript の組み込み機能を超える高度な型ユーティリティが必要な場合は、type-fest を検討してください。

  • Opaque<T, Token> - 手動の & { __brand } パターンよりも簡潔なブランド型
  • PartialDeep<T> - ネストされたオブジェクトに対する再帰的な部分型
  • ReadonlyDeep<T> - イミュータブルなデータに対する再帰的な readonly 型
  • SetRequired<T, K> / SetOptional<T, K> - 特定のフィールドを変更
  • Simplify<T> - IDE のツールチップで複雑な交差型をフラット化
import type { Opaque, PartialDeep } from 'type-fest';

type UserId = Opaque<string, 'UserId'>;
type UserPatch = PartialDeep<User>;

コメント