汎用的な CLAUDE.md は汎用的な TypeScript を生成します。Python バックエンドに有効なルールは TypeScript にそのまま転用できません。TypeScript 固有の障害モード、固有のアンチパターン、AI コーディングアシスタントに繰り返し伝える必要のある事項があります。
これはプロダクション向けに調整した TypeScript プロジェクト用 CLAUDE.md テンプレートです。自分のスタックに適用できるセクションを取って、残りは捨ててください。
コア型安全性ブロック
AI 生成 TypeScript で最も頻繁に見る問題: あちこちの any、null チェック漏れ、型ナローイングではなく型アサーション。以下のルールはこれらすべてに対処します。
## 型安全性
- **`any` を絶対に使わない**。型のないサードパーティライブラリとのブリッジ以外は禁止。やむを得ず `any` を使う場合は理由をインラインコメントで記載する。
- 形状不明の値には `unknown` を使う。型ガードで絞り込んでから使う。
- 型アサーションには `as` より `satisfies` を優先する。`as` はエラーを隠す。`satisfies` はエラーを検証する。
- ユーザー提供データや API レスポンスに非 null アサーション (`!`) を使わない。明示的に null チェックする。
- strict モード設定を使う: `strict: true`、`noUncheckedIndexedAccess: true`、`exactOptionalPropertyTypes: true`。
- ユニオンを絞り込む場合、すべてのケースを処理する。未知のバリアントをサイレントに落とすキャッチオールの `else` を使わない。
satisfies vs as の差が重要な理由
type Config = { port: number; host: string };
// NG: as は型エラーを隠す
const config = { port: "3000", host: "localhost" } as Config; // エラーなし、型が間違っている
// OK: satisfies は宣言時にエラーを捕捉する
const config = { port: 3000, host: "localhost" } satisfies Config; // 正しい
チームが as を習慣的に使ってきた場合、この違いを CLAUDE.md に追加してください。
インポートとモジュールのルール
## インポート
- `tsconfig.json` で定義したパスエイリアス(`@/components`、`@/lib` 等)を使う。2 ディレクトリ以上横断する相対パスは禁止。
- インポートが型アノテーションのみに使われる場合は `import type` を使う。循環参照によるランタイム問題を防ぎバンドルサイズを削減する。
- 1つのエクスポートだけ必要な場合にインデックスファイルからバレルインポートしない。ソースファイルから直接インポートする。
- インポートをグループ分けする: stdlib → サードパーティ → 内部。グループ間に1行の空行を入れる。
- モジュールの境界外から `../internal` や `../private` ディレクトリをインポートしない。
import type ルールの例:
// NG: 型だけ必要なときにランタイム値をインポートする
import { User } from "./user";
// OK: ランタイムコストゼロ
import type { User } from "./user";
// 値自体が必要な場合はランタイムインポート
import { createUser } from "./user";
エラーハンドリングパターン
AI 生成 TypeScript はよく any 型の裸の try/catch を使ったり、エラーをサイレントに飲み込んだりします。以下のルールはプロダクションで生き残るパターンを強制します。
## エラーハンドリング
- catch ブロックは `unknown` 型を使う: `catch (error: unknown)`。`catch (e: any)` は禁止。
- キャッチしたエラーのプロパティにアクセスする前に絞り込む: `if (error instanceof Error) { ... }`
- エラーをサイレントに飲み込まない。キャッチして再スローしない場合は、必ず可視化してログに記録または処理する。
- async 関数では、未処理の Promise rejection ハンドラーに頼らず明示的なエラーハンドリングを優先する。
- 失敗が想定される操作(ユーザー入力、ネットワークリクエスト)には型付き Result パターンを使う。制御フローに例外を使わない。
- 生の文字列をスローしない。`Error` インスタンスかカスタムエラークラスをスローする。
コードベースに含める価値のある型付き Result パターン:
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const user = await db.users.findById(id);
if (!user) return { ok: false, error: new Error(`User ${id} not found`) };
return { ok: true, value: user };
} catch (error: unknown) {
return { ok: false, error: error instanceof Error ? error : new Error(String(error)) };
}
}
非同期と Promise のルール
## Async/Await
- 失敗しうる非同期呼び出しはすべて `await` する。明示的なエラーハンドリングなしのファイアアンドフォーゲットは禁止。
- 同じ関数内で `.then()` チェーンと `await` を混在させない。
- 並行操作には、逐次 await の代わりに `Promise.all()`(部分的な失敗が許容される場合は `Promise.allSettled()`)を使う。
- 関数を `async` とマークするのは `await` を含む場合のみ。不必要に `Promise.resolve()` を返す非 async 関数にしない。
- キャンセル可能な非同期操作(fetch、長時間タイマー)には `AbortController` を使う。
関数コンポーネントのルール (React/Next.js)
プロジェクトが React を使う場合:
## React コンポーネント
- 関数コンポーネントのみ。クラスコンポーネント禁止。
- コンポーネントの型をコロケーション: `type Props = { ... }` をファイルの先頭に、別の型ファイルではなく。
- `children` の型付けが必要な場合のみ `React.FC` を使う。それ以外は Props を直接型付けする。
- イベントハンドラー: 関数名には `onClick` ではなく `handleClick` を使う。JSX では `onClick={handleClick}` と書く。
- `useEffect` の依存配列に渡されるコールバックにインライン関数定義を使わない。
- リストのキーは安定していてユニークでなければならない。配列インデックスはNG。
- `useEffect` のクリーンアップ: サブスクリプション・タイマー・イベントリスナーにはクリーンアップ関数を返す。
テスト基準
## テスト
- テストファイルはソースとコロケーション: `foo.ts` → `foo.test.ts`
- describe ブロックはテスト対象モジュールの名前: `describe('createUser', ...)`
- 各 `it` ブロックは1つの振る舞いをテストし1つのアサーショングループを持つ
- 境界でモック: HTTP は MSW、ファイルシステムは memfs、時間は `vi.useFakeTimers()` でモックする
- 実装の詳細ではなくパブリックインターフェースをテストする
- 型エラーを回避するためにテストで `as` や型アサーションを使わない。型を修正する。
- スナップショットテストは静的 UI コンポーネントのみ許可。API レスポンスやビジネスロジックは禁止。
禁止パターン
これらを明示的な禁止事項として CLAUDE.md に記載します。
## 使ってはいけないパターン
- `// @ts-ignore` — 型エラーを修正する。隠さない。本当に抑制が必要な場合は理由とともに `// @ts-expect-error` を使う。
- `Object.keys(obj).forEach` でキーの型が必要な場合 — 代わりに型付きの `for...of` ループを使う。
- バリデーションなしの `JSON.parse()` — スキーマライブラリ(zod、valibot)を使い結果をパースする。
- undefined チェックなしの `process.env.FOO` — 常にフォールバックを提供するか起動時にスローする。
- 関数の引数を変更する — 新しいオブジェクト/配列を返す。
- CSS-in-JS (styled-components、Emotion) での `!important` — ブルートフォースではなく詳細度を使う。
完全なテンプレート
以下はすべてを1つのドロップインブロックにまとめたものです。
## TypeScript ルール
### 型安全性
- `any` を使わない。`unknown` を使って絞り込む。
- 型アサーションには `as` より `satisfies` を優先する。
- ユーザーデータや API レスポンスに非 null アサーション `!` を使わない。
- ユニオンのすべてのケースを明示的に絞り込む。キャッチオールの `else` は禁止。
### インポート
- 深い相対パスよりパスエイリアスを使う。
- 型のみのインポートには `import type` を使う。
- 1つのエクスポートだけ必要な場合にバレルインポートしない。
### エラーハンドリング
- `catch (error: unknown)` — `catch (e: any)` は禁止。
- プロパティアクセス前にキャッチしたエラーを絞り込む。
- エラーのサイレント飲み込み禁止。
- 生の文字列をスロー禁止。
### 非同期
- 失敗しうる非同期呼び出しはすべて await する。
- 並行操作には `Promise.all` を使う。
- キャンセル可能な fetch には `AbortController` を追加する。
### テスト
- テストはソースとコロケーション。
- 境界でモック: HTTP は MSW、ファイルシステムは memfs。
- 型エラー回避のためにテストで `as` を使わない。
### 禁止
- `@ts-ignore`
- バリデーションなしの `JSON.parse()`
- undefined チェックなしの `process.env.FOO`
- 関数引数の変更
プロジェクトへのチューニング
上記テンプレートは最も高頻度の問題をカバーします。特定のプロジェクト向けに以下を追加してください。
- ORM/クエリパターン: 「WHERE 句には文字列テンプレートではなく Drizzle の
eq()を使う」 - 状態管理: 「サーバーステートは TanStack Query、クライアントステートは Zustand — React コンテキストには何も入れない」
- API 契約: 「すべての外部 API 呼び出しは
@/lib/apiモジュール経由 — コンポーネントで直接 fetch しない」 - バリデーションエントリポイント: 「すべての入力をルートハンドラーレイヤーで Zod を使ってパース・バリデーションしてからビジネスロジックに渡す」
CLAUDE.md の合計を 200 行以下に保ってください。それを超えたルールは希薄になります。多すぎるとどれに従うかを Claude Code が選ばなければならなくなる。守られなかった場合に最も大きなダメージをもたらすルールを優先してください。単なる好みではなく。