結論:Claude CodeはTypeScriptの型システムと相性が良い。型エラーをそのまま渡せば原因と修正案を返し、any まみれのコードに段階的に型を入れていける。ただし生成された型は「実行時の保証」ではないので、人のレビューと入力値の検証は必ず残す——というのが2026年6月時点でこの記事の中身です。
- 要点1:型は「実装前に契約を決める道具」。どこで何を防ぎたいかを先に言語化してからClaude Codeに渡すと精度が上がる。
- 要点2:ジェネリクスとユーティリティ型(
Partial/Pick/Recordなど)を活用すると、型定義の重複を減らせる。anyは「逃げ」なので最後の手段。 - 要点3:生成された型は鵜呑みにしない。tsc のエラーを一緒に読み、外部入力には実行時バリデーションを別途かける。
対象読者:TypeScriptを書く(または書き始めた)フロントエンド/バックエンドのエンジニア、型周りで手が止まりがちな人。
今日やること:手元のプロジェクトで tsc --noEmit を1回流し、出たエラーをそのままClaude Codeに貼って「原因と直し方を教えて」と聞いてみる。
「TypeScript使ってるのに、なぜか実行時に undefined でエラーが出る」「any が増殖して、もはや型の意味がない」——このあたりは型を入れ始めたチームが必ず一度は通る道です。型システムは強力なんですが、使いこなすにはそれなりの慣れが要る。そしてその「慣れ」の部分を、Claude Codeはかなり肩代わりしてくれます。
この記事では、Claude Codeを使ってTypeScriptの型システムを効率よく扱う進め方を、指示例とコード付きで整理します。型で何を防ぐのか、既存コードの型をどう改善するのか、ジェネリクスやユーティリティ型をどう使うのか、型エラーをどう読むのか、そして「やりすぎ」の落とし穴まで。順番に見ていきましょう。

そもそも型で「何を」防ぐのかを先に決める
型システムの目的は、ざっくり言えば「実行する前に、契約違反を見つけること」です。関数が受け取る引数の形、返す値の形、オブジェクトが持つべきプロパティ。これらをコンパイル時に検証して、undefined.name みたいな事故をデプロイ前に止める。それが型の役割です。
大事なのは、Claude Codeに型を書かせる前に「どこで何を防ぎたいか」を自分の言葉にしておくこと。漠然と「型つけて」と頼むより、防ぎたい事故を伝えたほうが、出てくる型の質が変わります。
たとえばAPIレスポンスを扱う関数。こう頼みます。
このfetchUser関数に型をつけてほしい。
防ぎたいのは「APIがnullを返したときにそのままUIに渡してクラッシュする」事故。
- 引数: userId は文字列
- 戻り値: ユーザーが見つからない可能性があるので User | null
- User型は id, name, email を必須、avatarUrl は省略可
呼び出し側でnullチェックを強制したいので、その形で型を設計して。
このレベルまで意図を渡すと、Claude Codeは単に any を消すだけでなく、「nullを返しうる」という設計判断まで型に落としてくれます。型は「コードの後付け」ではなく「設計の表現」だと考えると、指示も自然と具体的になります。
型の基本的な考え方や記法そのものをおさらいしたいときは、TypeScript公式のEveryday Typesを一度読んでおくと、Claude Codeへの指示も的確になります。
既存コードの型を把握し、段階的に改善する
新規より圧倒的に多いのが「すでにある any だらけのコードに型を入れていく」作業です。ここはClaude Codeの@ファイル参照とコードベース理解が効きます。
まず現状を把握させます。
@src/services/order.ts の型の状態を確認して。
- any や @ts-ignore がどこにあるか
- 型をつけると壊れそうな箇所はどこか
- 安全に型を入れられる順番を提案して
Claude Codeはファイルを読み、どこが型の「穴」になっているかを列挙します。ここでいきなり全部直そうとしないのがコツ。型付けは小さく、テストできる単位で進めるのが鉄則です(公式のCommon workflowsでもリファクタリングは「small, testable increments」で進めることが推奨されています)。
進め方は順を追ってこうなります。
tsconfig.jsonのstrict関連フラグを確認する(strictNullChecksが無効だとnull安全が効かない)。- 最も外側=入力に近い関数(APIレスポンス、フォーム入力、外部ライブラリの戻り値)から型をつける。
- 型をつけたら
tsc --noEmitを流し、新しく出たエラーを潰す。 - 既存テストを実行して、振る舞いが変わっていないことを確認する。
- 1ファイル分が緑になったら、そこで一度コミットして次へ進む。
「strict系フラグって結局どれを入れればいいの?」という設定の話は、TypeScript公式のtsconfigリファレンスで各フラグの効果を確認しながら、Claude Codeに「うちのプロジェクトに今入れるべきフラグはどれ?理由つきで」と聞くと判断しやすくなります。
このあたりの「既存コードを少しずつ良くする」流れは、レビュー観点でも同じ考え方が使えます。コードレビューをClaude Codeで回す具体的な手順はClaude Codeでコードレビューを効率化する実践ガイドにまとめてあるので、型改善とセットで読むと相性がいいです。
ジェネリクス・ユーティリティ型・anyの避け方
型を「重複なく、無理なく」書くための主役がジェネリクスとユーティリティ型です。ここはClaude Codeに任せると一気に楽になる領域なんですが、丸投げすると逆に読めない型になることもあるので、使いどころを押さえておきます。
ジェネリクスで「同じ形の処理」をまとめる
たとえばAPIラッパーが複数あって、戻り値の型だけが違う。こういうときはジェネリクスです。
// Before: 型ごとに関数が増える
function fetchUser(id: string): Promise<User> { /* ... */ }
function fetchOrder(id: string): Promise<Order> { /* ... */ }
// After: ジェネリクスで1つに
async function fetchResource<T>(path: string): Promise<T> {
const res = await fetch(path);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<T>;
}
// 呼び出し側で型を指定
const user = await fetchResource<User>("/api/users/1");
Claude Codeへの頼み方はこうです。「この3つのfetch関数、戻り値の型しか違わないのでジェネリクスで1つにまとめて。呼び出し側の型推論が効くようにして」。ポイントは「呼び出し側の型推論が効くように」を添えること。これを言わないと、使う側で毎回キャストが必要な微妙な設計になることがあります。
ジェネリクスの細かい挙動はTypeScript公式のGenericsハンドブックが一次情報です。生成された型が「なぜこう書かれているか」を確認するときに開くと理解が早いです。
ユーティリティ型で既存の型を使い回す
同じプロパティを何度も書くのは事故のもと。Partial、Pick、Omit、Record といったユーティリティ型を使うと、元の型から派生型を作れます。
interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}
// 更新APIは一部だけ送れればいい → Partial
type UserUpdate = Partial<Omit<User, "id" | "createdAt">>;
// 公開用は機微情報を除く → Pick
type PublicUser = Pick<User, "id" | "name">;
「User型から、更新可能なフィールドだけのUserUpdate型を作って。idとcreatedAtは更新不可なので除外」——このくらい具体的に頼めば、Claude Codeはユーティリティ型を組み合わせて返してくれます。どんなユーティリティ型があるかはTypeScript公式のUtility Types一覧で全体像をつかんでおくと、提案の良し悪しを判断できます。
anyは「最後の手段」、まずunknownを検討する
any は型チェックを完全に無効化するので、つけた瞬間そこは型の保護が消えます。どうしても型が分からない値(外部ライブラリの戻り値など)は、まず unknown を検討します。unknown は「使う前に型を絞れ」と強制してくれるので、anyより安全です。
// NG: anyで素通り
function parse(input: any) {
return input.data.items; // どんな事故も検知されない
}
// OK: unknownで絞り込みを強制
function parse(input: unknown) {
if (
typeof input === "object" && input !== null &&
"data" in input
) {
// ここで型を絞ってから使う
}
throw new Error("unexpected input shape");
}
Claude Codeに「このanyをunknownに変えて、使う前に型ガードを入れて」と頼むと、上のような絞り込みを自動で書いてくれます。型を絞るパターン(narrowing)の考え方はTypeScript公式のNarrowingに詳しいです。
型エラーの読み解きと対処
TypeScriptのエラーメッセージは、慣れないと暗号に見えます。「Type ‘X’ is not assignable to type ‘Y’」が3階層ネストして出てくると、心が折れる。ここがClaude Codeの一番ありがたい使いどころです。
やることはシンプルで、tsc --noEmit の出力をまるごと貼るだけ。
tsc --noEmit を流したらこのエラーが出た。
原因と直し方を、初心者にも分かるように説明して。
直すべきなのは型定義側か、呼び出し側か、それも教えて。
src/cart.ts:42:7 - error TS2345: Argument of type
'{ id: string; qty: number; }' is not assignable to
parameter of type 'CartItem'.
Property 'price' is missing in type
'{ id: string; qty: number; }' but required in type 'CartItem'.
Claude Codeは「CartItem型がpriceを必須にしているのに、呼び出し側で渡していない」と原因を特定し、「priceを渡す」「priceをオプショナルにする」のどちらが設計上正しいかまで一緒に考えてくれます。ここで丸ごと言いなりにならず、「priceは本当に省略していい値なのか?」を自分でも判断するのが大事。型を緩める修正は、たいてい後で別の事故を生みます。
テスト周りで型エラーと振る舞いの両方を一気に固めたいときは、Claude CodeでQA・テスト自動化を加速する実践ガイドの流れと組み合わせると、「型を直す→テストで振る舞いを担保する」が1セットで回せます。
落とし穴:複雑な型・生成された型・実行時の保証
ここまで便利な話をしてきましたが、型まわりでClaude Codeを使うときに必ず踏みやすい地雷が3つあります。
1. 過度に複雑な型を作りがち
Claude Codeは「できる」ので、頼むとconditional typesやmapped typesを駆使した職人芸みたいな型を平気で出してきます。動くんですが、半年後に読めない。型は「チームの誰もが読める」ことが第一です。
// ❌ 動くが、誰も保守できない型
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
// (こういうのが本当に必要なケースは稀)
// ⭕ まず素直な型で足りないか考える
type UserUpdate = Partial<User>;
対策は指示の段階で「読みやすさ優先で。conditional typesやmapped typesは本当に必要なときだけ。まずは標準のユーティリティ型で足りないか検討して」と一言添えること。これだけで出力の複雑さがだいぶ抑えられます。
2. 生成された型を鵜呑みにしない
Claude Codeが書いた型が、実際のデータと合っているとは限りません。特に外部APIの型は、ドキュメントと実物がズレていることが日常茶飯事。「APIはこの形を返すはず」という前提でClaude Codeが型を書いても、本番では別の形が来ることがあります。
生成された型は必ず人がレビューし、可能なら実際のレスポンスサンプルと突き合わせる。Claude Code公式も、生成物はそのまま使わずレビューしてからマージすることを推奨しています(Best practices)。型は「コードがどう動くべきか」の宣言であって、「外の世界が約束を守る保証」ではありません。
3. 型は実行時には消える——入力検証は別途必要
これが一番見落とされる点です。TypeScriptの型はコンパイル時にチェックされた後、JavaScriptに変換される段階できれいさっぱり消えます。つまり実行時には型は存在しない。User 型だと宣言しても、APIが壊れたJSONを返せば、その型は何も守ってくれません。
// 型は「コンパイル時の約束」でしかない
const user = await res.json() as User;
// ↑ 実際にUser型である保証はゼロ。jsonが何を返しても通る
// 外部入力には実行時バリデーションを別途かける(例: zod)
import { z } from "zod";
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
const user = UserSchema.parse(await res.json());
// ↑ ここで初めて「実行時に」形が保証される
Claude Codeに「APIレスポンスの型をつけて」と頼むと型注釈だけで終わることがあるので、「外部入力なので実行時バリデーションも入れて」と明示しましょう。型注釈と実行時検証は別物——ここを混同すると、型がついているのに本番で落ちる、という一番気持ちの悪いバグを生みます。
まとめ:型は「設計」、検証は「人とランタイム」
Claude CodeとTypeScriptの型システムは、組み合わせると本当に強力です。エラーの読み解き、any の段階的な解消、ジェネリクスやユーティリティ型の活用——これまで「型に詳しい人」がいないと進まなかった作業を、指示一つで前に進められます。
ただし忘れてはいけないのが、型は設計の表現であって魔法ではないこと。生成された型は人がレビューする。外部入力には実行時の検証をかける。複雑な型より読める型を優先する。この3つを守れば、型は「事故を未然に防ぐ味方」であり続けます。まずは手元のプロジェクトで tsc --noEmit を1回流して、出たエラーをClaude Codeに渡すところから始めてみてください。
フロントエンド開発全体の進め方とあわせて型戦略を整えたい場合はClaude Codeで爆速フロント開発|React/Next.jsを、Claude Codeの使い方を体系的に押さえたい場合はClaude Code 実践テクニック完全ガイドを参照してください。