typescript-best-practices について
コーディングエージェント向けの TypeScript スキルで、型を起点とする設計、実行時検証、言語固有の慣用パターンを扱います。React のファイルでは `react-best-practices` との併用を推奨します。
主な機能
- 01型を起点とするモデリング
- 02判別可能なユニオンとブランド型
- 03Zod による実行時検証
- 04網羅性を確認する switch
- 05任意の type-fest ユーティリティ
活用シーン
- 01不正な組み合わせを排除する状態設計
- 02実行時入力の検証と正規化
- 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>;
コメント
Markdown 対応