Cursor デバッグ指示テンプレ:最小再現・ログ挿入・原因仮説の書き方
AI エディタ Cursor を使ってデバッグを行う際、「うまく動かないんだけど」という曖昧な指示では、AI も適切な解決策を提案できません。 デバッグを効率的に進めるには、問題を明確に伝え、AI が原因を特定しやすい形で情報を提供することが重要です。
本記事では、Cursor でのデバッグ指示を効果的に行うための3 つのテンプレートをご紹介します。 最小再現コードの作り方、ログ挿入の指示方法、原因仮説の立て方を具体例とともに解説していきますので、明日からのデバッグ作業がグッと楽になるでしょう。
デバッグ指示テンプレート早見表
まずは、3 つのテンプレートの概要を一覧で確認できます。 状況に応じて適切なテンプレートを選択してください。
| # | テンプレート名 | 適用場面 | 主な構成要素 | 所要時間 | 効果 |
|---|---|---|---|---|---|
| 1 | 最小再現コード | エラーが明確で再現可能な場合 | 問題概要・再現手順・最小コード・エラーメッセージ・環境情報 | 15-30 分 | ★★★ |
| 2 | ログ挿入調査 | 問題箇所が不明で調査が必要な場合 | 調査対象・確認情報・ログ挿入箇所・既存コード・指示内容 | 10-20 分 | ★★☆ |
| 3 | 原因仮説検証 | ある程度見当がついている場合 | 問題説明・仮説(3 つ程度)・根拠・検証方法・依頼内容 | 10-15 分 | ★★★ |
テンプレート選択フローチャート
以下のフローで、どのテンプレートを使うべきか判断できます。
mermaidflowchart TD
start["デバッグが<br/>必要"] --> q1{"エラーメッセージは<br/>明確か?"}
q1 -->|はい| q2{"再現手順が<br/>わかるか?"}
q1 -->|いいえ| template2["テンプレート2:<br/>ログ挿入調査"]
q2 -->|はい| q3{"原因の見当は<br/>ついているか?"}
q2 -->|いいえ| template2
q3 -->|はい| template3["テンプレート3:<br/>原因仮説検証"]
q3 -->|いいえ| template1["テンプレート1:<br/>最小再現コード"]
template1 --> result["効率的な<br/>デバッグ"]
template2 --> result
template3 --> result
各テンプレートの必須項目チェックリスト
デバッグ指示を作成する際は、以下の項目を確認してください。
テンプレート 1:最小再現コード
| # | 項目 | 説明 | 必須度 |
|---|---|---|---|
| 1 | 問題の概要 | 1-2 文で問題を要約 | ★★★ |
| 2 | 再現手順 | 3-5 ステップで手順を明記 | ★★★ |
| 3 | 最小再現コード | 問題を再現する最小限のコード(50 行以内推奨) | ★★★ |
| 4 | 期待する動作 | 正常時の挙動を説明 | ★★☆ |
| 5 | 実際の動作 | 現在の挙動を説明 | ★★☆ |
| 6 | エラーメッセージ | 完全なエラーとスタックトレース | ★★★ |
| 7 | 環境情報 | 言語・FW・バージョン・OS | ★★☆ |
テンプレート 2:ログ挿入調査
| # | 項目 | 説明 | 必須度 |
|---|---|---|---|
| 1 | 調査対象 | 調査する処理・関数を明記 | ★★★ |
| 2 | 確認したい情報 | 3-5 個の確認ポイント | ★★★ |
| 3 | ログ挿入箇所の候補 | 3-5 箇所とその理由 | ★★★ |
| 4 | 既存コード | ログを挿入する対象コード | ★★★ |
| 5 | 指示内容 | ログの形式・出力方法を具体的に | ★★☆ |
テンプレート 3:原因仮説検証
| # | 項目 | 説明 | 必須度 |
|---|---|---|---|
| 1 | 発生している問題 | 問題の具体的な説明 | ★★★ |
| 2 | 仮説 1 | 1 つ目の原因仮説・根拠・検証方法 | ★★★ |
| 3 | 仮説 2 | 2 つ目の原因仮説・根拠・検証方法 | ★★★ |
| 4 | 仮説 3 | 3 つ目の原因仮説・根拠・検証方法 | ★★☆ |
| 5 | 依頼内容 | AI に求める提案内容 | ★★☆ |
よくある質問
デバッグ指示テンプレートの使い方に関する、よくある質問をまとめました。
| # | 質問 | 回答 |
|---|---|---|
| 1 | 複数のテンプレートを組み合わせても良いか? | はい。ログ挿入で調査後、原因仮説で検証する流れが効果的です |
| 2 | テンプレートの項目を省略しても良いか? | 必須度 ★★★ の項目は必ず記載してください。★★☆ は状況に応じて |
| 3 | コードが長い場合はどうするか? | 問題に関係ない部分を削除し、50 行以内に収めることを推奨します |
| 4 | エラーが出ない場合でも使えるか? | はい。ロジックエラーの場合は、期待動作と実際の動作の差分を明記してください |
| 5 | 仮説が 1 つしか思いつかない場合は? | 最低 2 つの仮説を立てることを推奨。AI に他の可能性を聞くのも有効です |
背景
Cursor でのデバッグが難しい理由
Cursor は ChatGPT や Claude などの LLM を活用した AI エディタで、コードの生成や修正を自然言語で指示できます。 しかし、デバッグの場面では「エラーが出る」「動かない」といった抽象的な指示だけでは、AI が問題の本質を理解できないことが多いです。
AI にとって、デバッグは以下のような情報が揃って初めて効果的な提案ができる作業です。
| # | 必要な情報 | 説明 |
|---|---|---|
| 1 | 再現手順 | どのような操作で問題が発生するか |
| 2 | エラー内容 | エラーメッセージやスタックトレースの詳細 |
| 3 | 期待する動作 | 本来どう動くべきか |
| 4 | 現在の動作 | 実際にどう動いているか |
| 5 | 環境情報 | 使用している言語、フレームワーク、バージョンなど |
デバッグ指示の 3 つのアプローチ
効果的なデバッグ指示には、大きく分けて 3 つのアプローチがあります。
mermaidflowchart TB
problem["デバッグが<br/>必要な状況"] --> approach1["最小再現コードを<br/>作成する"]
problem --> approach2["ログを挿入して<br/>調査する"]
problem --> approach3["原因仮説を<br/>立てる"]
approach1 --> result1["問題箇所を<br/>絞り込み"]
approach2 --> result2["実行時の<br/>状態を把握"]
approach3 --> result3["検証すべき<br/>ポイントを特定"]
result1 --> solution["効果的な<br/>デバッグ"]
result2 --> solution
result3 --> solution
上図は、デバッグの 3 つのアプローチとその成果を示しています。 それぞれのアプローチを状況に応じて使い分けることで、AI との協働デバッグがスムーズに進むでしょう。
課題
よくあるデバッグ指示の失敗パターン
Cursor でデバッグを依頼する際、以下のような指示では AI が適切な対応をしづらくなります。
パターン 1:情報不足の指示
typescript// ❌ 悪い例
'このコードが動きません。修正してください。';
このような指示では、どこが、どのように動かないのかが不明確です。 AI は推測で対応するしかなく、的外れな修正を提案してしまう可能性があります。
パターン 2:エラー情報の欠如
typescript// ❌ 悪い例
'エラーが出ます。';
エラーメッセージやスタックトレースがないと、AI は問題の種類すら判断できません。 型エラーなのか、実行時エラーなのか、ロジックエラーなのか、それぞれで対処法が異なります。
パターン 3:コード全体の丸投げ
typescript// ❌ 悪い例
'この500行のファイルのどこかにバグがあります。探してください。';
大量のコードを一度に渡されても、AI は問題箇所を特定するのに時間がかかります。 コンテキストが大きすぎると、重要な部分を見逃してしまうリスクもあるでしょう。
デバッグ効率が下がる要因
| # | 要因 | 影響 |
|---|---|---|
| 1 | 曖昧な指示 | AI が問題を誤解し、無関係な修正を提案 |
| 2 | 情報の分散 | 複数回のやり取りが必要になり時間ロス |
| 3 | 再現手順の欠如 | AI がローカルで問題を再現できず検証不可 |
| 4 | エラーコードの省略 | 検索性が低下し、類似事例を見つけにくい |
これらの課題を解決するには、構造化されたデバッグ指示テンプレートが有効です。
解決策
テンプレート 1:最小再現コードの作成指示
問題を最小限のコードで再現できれば、AI は余計な情報に惑わされず、核心部分に集中できます。
テンプレート構造
markdown# 問題の概要
[問題の簡潔な説明]
# 再現手順
1. [ステップ 1]
2. [ステップ 2]
3. [ステップ 3]
# 最小再現コード
[問題を再現する最小限のコード]
# 期待する動作
[本来どう動くべきか]
# 実際の動作
[実際にどう動いているか]
# エラーメッセージ
```
[完全なエラーメッセージとスタックトレース]
```
# 環境情報
- 言語/フレームワーク: [例: TypeScript 5.3, Next.js 14.0]
- パッケージマネージャー: [例: Yarn 4.0.2]
- OS: [例: macOS 14.1]
具体例:API レスポンスの型エラー
以下は、Next.js の API Routes で発生した型エラーを Cursor に報告する例です。
markdown# 問題の概要
Next.js の API Routes で fetch したデータの型が合わず、`TypeError` が発生しています。
# 再現手順
1. `/api/users` エンドポイントにアクセス
2. ユーザー一覧を取得
3. コンソールにエラーが表示される
# 最小再現コード
以下は問題が発生する API Routes のコードです。
typescript// pages/api/users.ts
import type { NextApiRequest, NextApiResponse } from 'next';
type User = {
id: number;
name: string;
email: string;
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
const users = await response.json();
// ここで型エラーが発生
const userNames = users.map((user: User) => user.name);
res.status(200).json({ names: userNames });
}
続いて、エラーメッセージと環境情報を記載します。
markdown# 期待する動作
ユーザー名の配列が JSON で返される
# 実際の動作
`TypeError: Cannot read property 'map' of undefined` が発生
# エラーメッセージ
```
TypeError: Cannot read property 'map' of undefined
at handler (pages/api/users.ts:15:25)
at async /node_modules/next/dist/server/api-utils.js:123:9
```
# 環境情報
- 言語/フレームワーク: TypeScript 5.3, Next.js 14.0.4
- パッケージマネージャー: Yarn 4.0.2
- Node.js: 20.10.0
- OS: macOS 14.1
このように情報を整理することで、AI は「fetch の結果が undefined の可能性がある」「型アサーションやガード処理が必要」といった具体的な提案をしやすくなります。
テンプレート 2:ログ挿入による調査指示
問題箇所が特定できない場合、戦略的にログを挿入して実行時の状態を確認する方法が効果的です。
テンプレート構造
markdown# 調査対象
[調査したい処理や関数]
# 確認したい情報
- [確認ポイント 1]
- [確認ポイント 2]
- [確認ポイント 3]
# ログ挿入箇所の候補
1. [箇所 1] - [理由]
2. [箇所 2] - [理由]
3. [箇所 3] - [理由]
# 既存コード
[ログを挿入する対象のコード]
# 指示
上記のコードに、確認したい情報がわかるようなログを挿入してください。
ログは `console.log` ではなく、デバッグ用のラベル付きで出力してください。
具体例:非同期処理の実行順序調査
以下は、Promise の実行順序が意図通りでない場合の調査指示です。
markdown# 調査対象
ユーザーデータ取得後にプロフィール画像をアップロードする処理
# 確認したい情報
- ユーザーデータ取得のタイミング
- プロフィール画像のアップロード開始時刻
- 各処理の完了順序
- エラーが発生した場合のエラー内容
typescript// lib/user.ts
async function updateUserProfile(
userId: string,
imageFile: File
) {
const user = await fetchUser(userId);
const imageUrl = await uploadImage(imageFile);
const updated = await updateUser(userId, {
profileImage: imageUrl,
});
return updated;
}
ログ挿入の候補箇所を明確にします。
markdown# ログ挿入箇所の候補
1. `fetchUser` 呼び出し前後 - ユーザーデータ取得のタイミングを確認
2. `uploadImage` 呼び出し前後 - 画像アップロードの開始・完了を確認
3. `updateUser` 呼び出し前後 - 更新処理の実行タイミングを確認
4. catch ブロック - エラー発生時の詳細情報を取得
# 指示
上記のコードに、以下の形式でログを挿入してください。
- `[DEBUG:関数名]` というプレフィックスをつける
- タイムスタンプを含める
- 引数と戻り値を出力する
- エラー時はスタックトレースも出力する
このような指示により、AI は以下のようなデバッグコードを生成できます。
typescript// lib/user.ts(ログ挿入後)
async function updateUserProfile(
userId: string,
imageFile: File
) {
console.log(
`[DEBUG:updateUserProfile] 開始 - userId: ${userId}, timestamp: ${new Date().toISOString()}`
);
try {
console.log(
`[DEBUG:fetchUser] 呼び出し前 - timestamp: ${new Date().toISOString()}`
);
const user = await fetchUser(userId);
console.log(
`[DEBUG:fetchUser] 完了 - user:`,
user,
`timestamp: ${new Date().toISOString()}`
);
console.log(
`[DEBUG:uploadImage] 呼び出し前 - timestamp: ${new Date().toISOString()}`
);
const imageUrl = await uploadImage(imageFile);
console.log(
`[DEBUG:uploadImage] 完了 - imageUrl: ${imageUrl}, timestamp: ${new Date().toISOString()}`
);
console.log(
`[DEBUG:updateUser] 呼び出し前 - timestamp: ${new Date().toISOString()}`
);
const updated = await updateUser(userId, {
profileImage: imageUrl,
});
console.log(
`[DEBUG:updateUser] 完了 - updated:`,
updated,
`timestamp: ${new Date().toISOString()}`
);
return updated;
} catch (error) {
console.error(
`[DEBUG:updateUserProfile] エラー発生 - timestamp: ${new Date().toISOString()}`
);
console.error(
`[DEBUG:updateUserProfile] エラー詳細:`,
error
);
console.error(
`[DEBUG:updateUserProfile] スタックトレース:`,
error instanceof Error
? error.stack
: 'スタック情報なし'
);
throw error;
}
}
この形式でログを挿入することで、各処理の実行順序や所要時間、エラー発生箇所を正確に把握できるようになります。
テンプレート 3:原因仮説の提示と検証依頼
ある程度問題の見当がついている場合、仮説を立てて AI に検証を依頼する方法が効率的です。
テンプレート構造
markdown# 発生している問題
[問題の具体的な説明]
# 原因の仮説
## 仮説 1:[仮説の名前]
- 根拠:[なぜそう考えたか]
- 検証方法:[どうやって確認するか]
## 仮説 2:[仮説の名前]
- 根拠:[なぜそう考えたか]
- 検証方法:[どうやって確認するか]
## 仮説 3:[仮説の名前]
- 根拠:[なぜそう考えたか]
- 検証方法:[どうやって確認するか]
# 依頼内容
上記の仮説について、それぞれ検証するためのコードや手順を提案してください。
また、他に考えられる原因があれば教えてください。
具体例:React の無限ループ問題
以下は、React コンポーネントが無限レンダリングする問題の原因仮説です。
markdown# 発生している問題
React コンポーネントが無限にレンダリングされ、ブラウザが固まります。
`useEffect` を使ってデータ取得を行っている箇所が原因と思われます。
問題が発生しているコードを提示します。
typescript// components/UserList.tsx
import { useState, useEffect } from 'react';
type User = {
id: number;
name: string;
};
export default function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [filter, setFilter] = useState({ role: 'admin' });
useEffect(() => {
fetch(`/api/users?role=${filter.role}`)
.then((res) => res.json())
.then((data) => setUsers(data));
}, [filter]); // ← ここが問題?
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
次に、原因の仮説を複数立てます。
markdown# 原因の仮説
## 仮説 1:依存配列のオブジェクト参照問題
- 根拠:`filter` がオブジェクトで、レンダリングごとに新しい参照が作られている可能性
- 検証方法:`useEffect` 内で `console.log` を追加し、何回実行されるか確認する
## 仮説 2:state 更新のタイミング問題
- 根拠:`setUsers` が実行されるとコンポーネントが再レンダリングされ、`useEffect` が再実行される
- 検証方法:依存配列を空にして、データ取得が 1 回のみ実行されるか確認する
## 仮説 3:API レスポンスの問題
- 根拠:API が毎回異なるデータを返し、state が更新され続けている
- 検証方法:取得したデータを `console.log` で確認し、内容が変化しているか調べる
# 依頼内容
上記の仮説について、それぞれ検証するためのコードを提案してください。
また、他に考えられる原因(例:依存配列の設定ミス、メモ化の不足など)があれば教えてください。
この指示により、AI は以下のような検証コードや対処法を提案できます。
typescript// 仮説1の検証コード
useEffect(() => {
console.log('[DEBUG] useEffect実行回数をカウント');
console.log('[DEBUG] filterの参照:', filter);
fetch(`/api/users?role=${filter.role}`)
.then((res) => res.json())
.then((data) => setUsers(data));
}, [filter]);
typescript// 仮説1の対処法:useMemo でオブジェクトをメモ化
import { useState, useEffect, useMemo } from 'react';
export default function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [role, setRole] = useState('admin');
// オブジェクトではなくプリミティブ値を依存配列に入れる
useEffect(() => {
fetch(`/api/users?role=${role}`)
.then((res) => res.json())
.then((data) => setUsers(data));
}, [role]); // ← 文字列なので参照が変わらない
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
仮説を立てることで、AI は検証の方向性を理解し、より的確なデバッグ支援を提供できるようになります。
具体例
ケーススタディ 1:Next.js の getServerSideProps エラー
実際のプロジェクトで発生した、getServerSideProps での型エラーを Cursor で解決した事例です。
問題の状況
Next.js でページコンポーネントを作成していたところ、以下のエラーが発生しました。
plaintextError: `getServerSideProps` returned `undefined` in `/dashboard`
Cursor への指示(最小再現テンプレート使用)
以下は、問題を構造化して Cursor に伝えた内容です。
markdown# 問題の概要
`getServerSideProps` が `undefined` を返しており、ページが表示されません。
エラーコード: `Error: getServerSideProps returned undefined`
# 再現手順
1. `/dashboard` ページにアクセス
2. サーバー側でデータ取得を試みる
3. エラーが表示され、ページがレンダリングされない
問題が発生するコードを提示します。
typescript// pages/dashboard.tsx
import type { GetServerSideProps } from 'next';
type Props = {
username: string;
};
export const getServerSideProps: GetServerSideProps<
Props
> = async (context) => {
const { userId } = context.query;
if (!userId) {
return {
redirect: { destination: '/login', permanent: false },
};
}
const res = await fetch(
`https://api.example.com/users/${userId}`
);
const user = await res.json();
// ここで return を忘れている
{
props: {
username: user.name;
}
}
};
export default function Dashboard({ username }: Props) {
return <div>Welcome, {username}</div>;
}
エラー情報と環境を記載します。
markdown# 期待する動作
ユーザー名を取得して Dashboard ページに表示される
# 実際の動作
`Error: getServerSideProps returned undefined` が発生し、ページが表示されない
# エラーメッセージ
```
Error: `getServerSideProps` returned `undefined` in `/dashboard`
at Object.renderToHTML (/node_modules/next/dist/server/render.js:567:23)
at async /node_modules/next/dist/server/next-server.js:1243:30
```
# 環境情報
- 言語/フレームワーク: TypeScript 5.3.3, Next.js 14.0.4
- パッケージマネージャー: Yarn 4.0.2
- Node.js: 20.10.0
Cursor の提案と解決
Cursor は即座に「return 文が欠けている」と指摘し、以下の修正を提案しました。
typescript// pages/dashboard.tsx(修正後)
export const getServerSideProps: GetServerSideProps<
Props
> = async (context) => {
const { userId } = context.query;
if (!userId) {
return {
redirect: { destination: '/login', permanent: false },
};
}
const res = await fetch(
`https://api.example.com/users/${userId}`
);
const user = await res.json();
// return を追加
return { props: { username: user.name } };
};
この修正により、エラーは解消されました。 最小再現コードとエラーメッセージを提示したことで、AI は瞬時に問題を特定できたのです。
ケーススタディ 2:React Hook の依存配列問題
useEffect の依存配列に関する警告が出ていたが、原因がわからなかったケースです。
問題の状況
ESLint が以下の警告を出力していました。
plaintextReact Hook useEffect has a missing dependency: 'fetchData'.
Either include it or remove the dependency array. (react-hooks/exhaustive-deps)
Cursor への指示(原因仮説テンプレート使用)
ログを挿入して調査する方針で Cursor に依頼しました。
markdown# 調査対象
データ取得処理を行う `useEffect` フック
# 確認したい情報
- `fetchData` 関数が何回定義されるか
- `useEffect` が何回実行されるか
- `fetchData` の参照が変わっているか
問題のコードを提示します。
typescript// components/DataViewer.tsx
import { useState, useEffect } from 'react';
export default function DataViewer({
apiUrl,
}: {
apiUrl: string;
}) {
const [data, setData] = useState(null);
const fetchData = async () => {
const res = await fetch(apiUrl);
const json = await res.json();
setData(json);
};
useEffect(() => {
fetchData();
}, []); // ← ESLint 警告: fetchData が依存配列にない
return <div>{JSON.stringify(data)}</div>;
}
調査箇所と指示を明確にします。
markdown# ログ挿入箇所の候補
1. `fetchData` 関数の定義直後 - 関数が何回作られるか確認
2. `useEffect` の内部 - 何回実行されるか確認
# 指示
上記のコードに、以下の情報を出力するログを挿入してください。
- `fetchData` 関数のインスタンス ID(参照が変わっているか確認するため)
- `useEffect` の実行回数
Cursor の提案と解決
Cursor は以下のデバッグコードを提案しました。
typescript// components/DataViewer.tsx(ログ挿入版)
import { useState, useEffect, useRef } from 'react';
export default function DataViewer({
apiUrl,
}: {
apiUrl: string;
}) {
const [data, setData] = useState(null);
const renderCount = useRef(0);
const effectCount = useRef(0);
renderCount.current += 1;
console.log(
`[DEBUG] レンダリング回数: ${renderCount.current}`
);
const fetchData = async () => {
console.log(`[DEBUG] fetchData 呼び出し`);
const res = await fetch(apiUrl);
const json = await res.json();
setData(json);
};
console.log(
`[DEBUG] fetchData 関数定義 - 参照ID: ${fetchData
.toString()
.slice(0, 50)}`
);
useEffect(() => {
effectCount.current += 1;
console.log(
`[DEBUG] useEffect 実行回数: ${effectCount.current}`
);
fetchData();
}, []);
return <div>{JSON.stringify(data)}</div>;
}
ログを確認すると、「レンダリングごとに fetchData の参照が変わっている」ことが判明しました。
Cursor は続けて以下の根本的な解決策を提案してくれました。
typescript// components/DataViewer.tsx(修正版)
import { useState, useEffect, useCallback } from 'react';
export default function DataViewer({
apiUrl,
}: {
apiUrl: string;
}) {
const [data, setData] = useState(null);
// useCallback でメモ化して参照を固定
const fetchData = useCallback(async () => {
const res = await fetch(apiUrl);
const json = await res.json();
setData(json);
}, [apiUrl]); // apiUrl が変わったときのみ再生成
useEffect(() => {
fetchData();
}, [fetchData]); // fetchData を依存配列に追加しても安全
return <div>{JSON.stringify(data)}</div>;
}
ログ挿入指示により、問題の原因を明確にし、適切な解決策にたどり着くことができました。
ケーススタディ 3:型推論の問題
TypeScript の型推論がうまくいかず、不要な型アサーションが必要になっていたケースです。
問題の状況
API レスポンスの型を正しく推論できず、毎回 as でキャストする必要がありました。
typescript// lib/api.ts
async function getUser(id: string) {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
return data; // ← 型が any になる
}
// 使用箇所
const user = (await getUser('123')) as User; // ← 毎回キャストが必要
Cursor への指示(原因仮説テンプレート使用)
仮説を立てて検証を依頼しました。
markdown# 発生している問題
API レスポンスの型が推論されず、`any` になってしまいます。
毎回型アサーション(`as User`)が必要で、型安全性が低下しています。
# 原因の仮説
## 仮説 1:戻り値の型注釈がない
- 根拠:関数の戻り値を明示していないため、推論が `Promise<any>` になる
- 検証方法:戻り値の型を明示的に指定して、型チェックが通るか確認
## 仮説 2:`res.json()` の型が不明
- 根拠:`fetch` の `json()` メソッドは `any` を返すため、型情報が失われる
- 検証方法:ジェネリクスや型ガードを使って型を絞り込む
## 仮説 3:API レスポンスのスキーマ検証がない
- 根拠:実行時にレスポンスの型を検証していないため、型安全性が保証されない
- 検証方法:Zod などのスキーマ検証ライブラリを導入して実行時チェックを追加
# 依頼内容
上記の仮説について、それぞれ解決策を提案してください。
特に、型安全性を保ちながら、キャストを減らす方法を知りたいです。
Cursor の提案と解決
Cursor は 3 つの解決策を提案しました。
解決策 1:戻り値の型注釈を追加
typescript// lib/api.ts
type User = {
id: string;
name: string;
email: string;
};
async function getUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
return data; // ← User 型として扱われる
}
解決策 2:ジェネリック関数で汎用化
typescript// lib/api.ts
async function fetchJson<T>(url: string): Promise<T> {
const res = await fetch(url);
const data = await res.json();
return data as T;
}
// 使用例
const user = await fetchJson<User>(`/api/users/${id}`);
解決策 3:Zod でスキーマ検証を追加(最も型安全)
まず、Zod をインストールします。
bashyarn add zod
スキーマを定義し、実行時検証を行います。
typescript// lib/api.ts
import { z } from 'zod';
// スキーマ定義
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
// スキーマから型を生成
type User = z.infer<typeof UserSchema>;
async function getUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
// 実行時に型検証
const validatedUser = UserSchema.parse(data);
return validatedUser;
}
Zod を使うことで、以下のメリットが得られます。
| # | メリット | 説明 |
|---|---|---|
| 1 | 型安全性の向上 | 実行時にスキーマ違反を検出できる |
| 2 | 型定義の一元化 | スキーマから TypeScript の型を自動生成 |
| 3 | エラー情報の充実 | どのフィールドが不正かを詳細に報告 |
Cursor は仮説に基づいて複数の解決策を提示し、最も型安全な方法を推奨してくれました。
デバッグ指示の比較表
最後に、3 つのテンプレートの使い分けを整理します。
| # | テンプレート | 適用場面 | メリット | デメリット |
|---|---|---|---|---|
| 1 | 最小再現コード | エラーが明確で再現手順がわかる場合 | 問題箇所を絞り込みやすい | 最小化に時間がかかる |
| 2 | ログ挿入 | 問題箇所が不明で調査が必要な場合 | 実行時の状態を詳細に把握できる | ログが多すぎると見づらい |
| 3 | 原因仮説 | ある程度見当がついている場合 | 効率的に検証できる | 仮説が外れると遠回りする |
状況に応じて適切なテンプレートを選ぶことで、デバッグの効率が大きく向上するでしょう。
まとめ
Cursor でのデバッグを成功させる鍵は、AI に対して「明確で構造化された情報」を提供することです。
本記事では、以下の 3 つのテンプレートをご紹介しました。
- 最小再現コードテンプレート - 問題を最小限のコードで再現し、エラー情報と環境を明示する
- ログ挿入テンプレート - 調査箇所を特定し、実行時の状態を可視化する
- 原因仮説テンプレート - 複数の仮説を立て、AI に検証方法を提案してもらう
これらのテンプレートを使うことで、曖昧な「動きません」という指示から、「この関数の戻り値が undefined になる原因を、依存配列の参照問題とエラーハンドリングの観点から検証してください」という具体的な指示に変わります。
AI は明確な指示に対して、驚くほど的確な解決策を提案してくれるでしょう。 明日からのデバッグ作業で、ぜひこれらのテンプレートを活用してみてください。
関連リンク
articleCursor デバッグ指示テンプレ:最小再現・ログ挿入・原因仮説の書き方
articleCursor × Monorepo 構築:Yarn Workspaces/Turborepo/tsconfig path のベストプラクティス
articleGitHub Copilot Workspace と Cursor/Cline の比較検証:仕様駆動の自動化能力はどこまで?
articleCursor の自動テスト生成を検証:Vitest/Jest/Playwright のカバレッジ実測
articleCursor で差分が崩れる/意図しない大量変更が入るときの復旧プレイブック
articleCursor × 生成 AI で変わる開発フロー:要件定義からレビューまでの新常識
articleDeno/Bun/Node のランタイムで共通動く Zod 環境のセットアップ
articleFFmpeg マッピング完全攻略:-map/-disposition/-metadata の黄金レシピ
articleYarn PnP で「モジュールが見つからない」時の解決大全:packageExtensions/patch で対処
articleESLint no-restricted-* 活用レシピ集:API 禁止・依存制限・危険パターン封じ込め
articleWeb Components のポリフィル戦略:@webcomponents 系を最小限で入れる判断基準
articleDify ワークフロー定型 30:分岐・並列・リトライ・サーキットブレーカの型
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来