はじめに
Claude CodeにTypeScriptを書かせると、こんなコードが出てくることがある。
function fetchUser(id: any): any {
return fetch(/api/users/${id}).then(res => res.json());
}
anyだらけだ。strict modeを有効にしたら数百のエラーが出た——という経験をしたエンジニアは少なくない。
これはClaude Codeの能力の問題ではなく、明示しなければstrict modeを前提にしないという設計上の特性だ。anyは型エラーを最も手っ取り早く回避する手段であり、レビューなしに型安全チェックが走らない環境では自然と増殖する。
この記事では、tsconfig設定・プロンプト設計・静的検査ループという3つのレイヤーで型品質を担保する方法を解説する。軸に置くのはBoris Tane(9ヶ月のClaude Code実践者)が全セッションで使っている実装プロンプトだ。
第1レイヤー:tsconfig.jsonの段階的strict化
既存プロジェクトに一気に strict: true を設定すると、数百〜数千のエラーが一度に出現する。Claude Codeでも対処できなくなる。3段階で段階的に有効化する。
Phase 1: 暗黙のanyを排除
{
"compilerOptions": {
"noImplicitAny": true
}
}
Claude Codeへの依頼:「noImplicitAnyエラーを全て修正して。anyを使わず、正確な型を推論して定義して」
Phase 2: nullの安全性を確保
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitReturns": true
}
}
Claude Codeへの依頼:「nullチェックを追加して。optional chainingとnullish coalescing演算子を活用して」
Phase 3: 完全strictモード
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
tsconfig設定と合わせて、CLAUDE.mdに型安全ルールを宣言しておくと全セッションで自動的に遵守される。
## TypeScript型安全ルール
環境
- tsconfig.json で strict: true が有効
- noUncheckedIndexedAccess: true(配列アクセスは必ずundefined確認が必要)
必須制約
- any型の使用禁止(ESLintでerrorレベルで強制済み)
- unknown型もtype guardなしで使用禁止
- 型アサーション(as)は最小限に。使う場合は必ずコメントで理由を書く
推奨パターン
- IDはBranded Types(Brand<string, 'XxxId'>)を使う
- Discriminated Unionでエラー型を設計する
- satisfies演算子を積極的に使う(型推論を保ちながら型検証)
実装プロセス
- 新機能はまず型設計(plan.mdに型定義を書く)→承認→実装
- 実装中は継続的に tsc --noEmit を実行
- pre-commitでtsc + ESLintが通らなければコミット不可
第2レイヤー:プロンプト設計で型品質を制御する
Boris Taneの標準実装プロンプト
Claude Codeを9ヶ月使い続けているBoris Tane(外部実践者)が全セッションで使っている実装プロンプトだ。
implement it all. when you're done with a task or phase,
mark it as completed in the plan document.
do not stop until all tasks and phases are completed.
do not add unnecessary comments or jsdocs,
do not use any or unknown types.
continuously run typecheck to make sure you're not
introducing new issues.
このプロンプトで型安全に関わるのは2つの制約で、個人的にはここが核心だと思っている。
| 指示 | なぜ重要か |
|---|---|
do not use any or unknown types | anyは型システムをバイパスする。unknownも多用するとtype guardが増えすぎる |
continuously run typecheck | 最後にまとめて修正ではなく、実装しながら即座に型エラーを修正させる |
「実装しながら継続的にtypecheckを実行する」という指示が特に効いている。最後にまとめて修正する方式だと、修正が積み重なって手に負えなくなる。実装のたびに型チェックが走ることで、問題を小さいうちに潰せる。
新機能の型設計に使うプロンプトはこうなる。
この機能を実装する前に、型設計だけを行って(コードは書かない):
- 使用するドメイン型(UserId・OrderId等のBrandedタイプ含む)
- 関数シグネチャ(入出力の型)
- APIレスポンスの型定義
- エラー型の設計
まずplan.mdに型設計を書いて。承認してから実装に入る。
補足:mcpmarket.comに「TypeScript Type Safety Expert」「TypeScript Error Fixer」などの専用スキルが公開されており、Branded TypesやDiscriminated Unionの実装・型エラーの自動修正をスキルとして呼び出す選択肢もある。
第3レイヤー:静的検査の自走ループ
AIコード固有のanyアンチパターン
OX Security調査と実践報告から分かるAI生成コード固有の型問題がある。
| アンチパターン | 対策 |
|---|---|
| TypeScript any乱用 | @typescript-eslint/no-explicit-any をerrorで強制 |
| 型アサーション(as any)乱用 | @typescript-eslint/consistent-type-assertions を強制 |
| 型定義のコメント洪水 | 無駄なJSDocを禁止(jsdoc/require-jsdoc 無効化) |
エージェントはエラーを消すためにESLint設定ファイル(.eslintrc)を変更しようとすることがある。PreToolUseフックで設定ファイルの編集をブロックしておく。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "file=$(jq -r '.tool_input.file_path // .tool_input.path // \"\"'); if echo \"$file\" | grep -qE '\\.(eslintrc|eslintignore|biome\\.json|tsconfig)'; then echo 'lint設定の変更は禁止' >&2; exit 2; fi"
}]
}
]
}
}
静的検査による自走ループ
AIがコードを書く
↓
tsc --noEmit(型チェック)が失敗
↓
Claude Codeがエラーを確認
↓
自動修正してから再実行
↓(成功)
ESLintでanyチェック
↓(クリア)
コミット
pre-commitへの組み込みはHuskyを使うと簡単だ。
# .husky/pre-commit
#!/bin/sh
npx tsc --noEmit && npx eslint --max-warnings 0
Claude Codeはpre-commitが失敗すると自動的にエラーを読んで修正を試みる。「失敗→修正→再試行」ループが型安全を保証する自走機構になる。Boris Taneの continuously run typecheck という指示と、このpre-commitフックを組み合わせることで、実装フェーズから最終コミットまで型安全が途切れない状態を作れる。
Branded Types と Total Type Safety
Branded Typesでドメインロジックを型に落とす
stringで済ませると引数の取り違えが型エラーになれない。
// ❌ UserId と OrderId が混在しても型エラーにならない
function getOrder(userId: string, orderId: string) { ... }
getOrder(orderId, userId); // 引数を逆にしても通ってしまう
// ✓ Branded Typesで区別する
type Brand<T, K> = T & { readonly __brand: K };
type UserId = Brand<string, 'UserId'>;
type OrderId = Brand<string, 'OrderId'>;
function getOrder(userId: UserId, orderId: OrderId) { ... }
// getOrder(orderId, userId); // 型エラーで検出される
Claude Codeへの依頼はこうなる。
UserService・OrderServiceの関数シグネチャで使われている全てのstring型のIDを
Branded Typesに置き換えて。Brand<string, 'UserId'>パターンを使って。
型定義ファイルはsrc/types/brands.tsに集約して。
TypeScript 5.x の satisfies 演算子と Total Type Safety
satisfies 演算子は型推論を保ちながら型制約を検証できる。CLAUDE.mdに推奨パターンとして宣言しておくと自動で使ってくれる。
const config = {
host: "localhost",
port: 3000,
} satisfies ServerConfig;
// config.port は ServerConfigではなく number型として推論される
2026年のトレンドとしてtRPC + TypeScript 5.x の組み合わせによる「Total Type Safety」がある。サーバー側で定義した型がHTTP境界を越えてフロントエンドまで型安全に推論される設計だ。Claude Codeへの指示では「tRPCルーターの型定義からfrontendのhooksまで、型が切れ目なく繋がるように実装して」と明示する。
Before/After
Before(指示なしのClaude Code TypeScript)
function processOrder(order: any, user: any): any {
const items = order.items;
return items.map((item: any) => ({
...item,
total: item.price * item.quantity
}));
}
After(型安全設定 + プロンプト制御)
type OrderId = Brand<string, 'OrderId'>;
type UserId = Brand<string, 'UserId'>;
interface OrderItem {
readonly id: string;
readonly price: number;
readonly quantity: number;
}
interface Order {
readonly id: OrderId;
readonly userId: UserId;
readonly items: readonly OrderItem[];
}
interface ProcessedItem extends OrderItem {
readonly total: number;
}
function processOrder(order: Order, user: User): readonly ProcessedItem[] {
return order.items.map((item) => ({
...item,
total: item.price * item.quantity,
} satisfies ProcessedItem));
}
まとめ
Claude Codeがデフォルトで型安全でない理由は、指示しなければstrict modeを前提にしないからだ。設定してしまえば、あとはちゃんと型安全なコードを生成してくれる。最初の数時間の手間を惜しんでanyだらけのコードを量産し続けるより、一度設定に投資する方が絶対に得だと感じている。
どこから手をつけるかはプロジェクトの状況によるが、迷ったらこの順番がやりやすい。
今すぐ(10分):CLAUDE.mdに「any型使用禁止」ルールを追記し、次のClaude Codeセッションから適用する。これだけで新規生成コードのany混入が抑制される。
今日中:tsconfig.json に "noImplicitAny": true を追加し、Claude Codeにエラーを修正させる。Phase 1から始めることで一度に大量のエラーを抱えなくて済む。
チーム展開:Boris Taneの実装プロンプトを自プロジェクト用にカスタマイズして使い始め、.eslintrc に @typescript-eslint/no-explicit-any: error を追加し、PreToolUseフックでESLint設定の変更をブロックする。「AIが型設定を書き換えて逃げる」問題を構造的に防ぐ。
なお、型安全強化で「エラーが90%削減した」という報告(Zenn 2026年)もあるが、これは1事例の参考値だ。自プロジェクトでの計測が実態を把握する最善策になる。

コメント