0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIが書いたTypeScriptの「赤い波線」をanyで握りつぶさない:Claude Codeに"型で"直させるプロンプト

0
Posted at

未経験の方がClaude CodeやCopilotでTypeScriptを書き始めると、動くコードはすぐ出るのに、エディタに**赤い波線(型エラー)**が残ることがよくあります。

このとき一番やりがちなのが、any@ts-ignore で消してしまうこと。実際、AI自身に「エラー消して」と頼むと、平気で any を差し込んで返してくることがあります。動いてしまうので気づきにくいのですが、これはTypeScriptを入れた意味を自分で捨てている状態です。

この記事では、AIが生成したコードで頻出する型エラーを3つ、実際のコード片とエラーメッセージで示し、「なぜ握りつぶすと危険か」→「AIにどう頼めばanyを使わず型で直せるか」を深掘りします。

前提:any@ts-ignore が"消してるだけ"な理由

TypeScriptの型エラーは、多くの場合「実行時に落ちる可能性がある箇所」を事前に教えてくれています。any を入れると、その箇所の型チェックが丸ごと無効化され、以降のプロパティアクセスも一切検査されなくなります。@ts-ignore は、その行のエラーを理由を問わず黙らせるだけです。

つまり、赤い波線は消えますが、バグの可能性はコードに残ったままになります。しかも消えているので、後から自分でも気づけません。

まずは tsconfig.jsonstrict: true(最低でも strictNullChecks: true)が入っている前提で話を進めます。これが無いと、そもそも危険なエラーが波線として出てきません。

{
  "compilerOptions": {
    "strict": true
  }
}

ケース1:Object is possibly 'null'

DOM操作やAPIのレスポンスで最頻出です。AIに「フォームの値を取得して」と頼むと、こういうコードが返ってきがちです。

const input = document.getElementById("email");
console.log(input.value);
//          ~~~~~ Object is possibly 'null'.

getElementById は要素が無ければ null を返すので、戻り値の型は HTMLElement | nullnull のときに .value を触ると実行時に Cannot read properties of null で落ちます。TypeScriptはそれを事前に警告しています。

未経験がやりがちなミス

const input = document.getElementById("email") as any;
console.log(input.value); // 波線は消えるが、nullなら本番で落ちる

as any で波線は消えますが、inputnull の可能性は何も解決していません。IDのタイプミス一つで本番エラーです。

型で直す

null のケースを明示的に処理し、要素の種類も絞り込みます。

const input = document.getElementById("email");
if (input instanceof HTMLInputElement) {
  console.log(input.value); // ここでは input は HTMLInputElement 確定
} else {
  console.warn("email input が見つかりません");
}

instanceof で絞り込むと、null でないことに加えて「.value を持つ HTMLInputElement である」ことまで型で保証されます。

AIへの頼み方

このコードで "Object is possibly 'null'" が出ています。
input が null の場合を握りつぶさず処理してください。
また .value を使いたいので HTMLInputElement であることを型で絞り込んでください。
any と @ts-ignore は使わないでください。

ポイントは、**状況(何のエラーか)+期待する型(HTMLInputElementを使いたい)+禁止事項(any/ts-ignore禁止)**をセットで渡すことです。ここまで指定しないと、AIは楽な方(any)に逃げます。

ケース2:Type 'string | undefined' is not assignable to type 'string'

環境変数や配列アクセス、オプショナルなプロパティで頻出します。

function greet(name: string) {
  return `Hello, ${name}`;
}

const user = { name: "Sato", nickname: undefined as string | undefined };
greet(user.nickname);
//    ~~~~~~~~~~~~~ Argument of type 'string | undefined'
//                  is not assignable to parameter of type 'string'.

nicknameundefined かもしれないのに、greetstring しか受け取りません。

未経験がやりがちなミス

greet(user.nickname!); // ! で「絶対ある」と言い切る

!(非nullアサーション)は as any に近く、「無いはずない」と開発者が保証するという意味です。実際に undefined が来たら、やはり実行時に落ちます。

型で直す

「無いとき」の値を用意するか、関数側で undefined を受け入れます。

// A: 呼び出し側でフォールバック
greet(user.nickname ?? user.name);

// B: 関数定義を差し替え、undefined を許容する設計にする
function greet(name: string | undefined) {
  return `Hello, ${name ?? "guest"}`;
}

??(Null合体演算子)は「左が null/undefined のときだけ右を使う」ので、型が string に確定します。

AIへの頼み方

"Type 'string | undefined' is not assignable to type 'string'" が出ています。
undefined の可能性を ! で潰さず、undefined のときのフォールバック値を
?? で用意する形に直してください。any と ! と @ts-ignore は禁止です。

! も明示的に禁止に入れるのがコツです。禁止しないとAIは ! で"それっぽく"直してきます。

ケース3:Property 'xxx' does not exist on type

AIがAPIレスポンスを扱うコードを書くと、これがよく出ます。

async function getUser() {
  const res = await fetch("/api/user");
  const data = await res.json(); // data の型は any
  return data.userName;          // any なので何も検査されない
}

res.json() の戻り値は any(正確には Promise<any>)です。ここで data.userName と書けてしまいますが、実際のAPIが user_name を返していたら undefined。エラーも波線も出ず、一番見つけにくいバグになります。逆に dataunknown で受けてからプロパティに触ろうとすると、今度は 'data' is of type 'unknown'.(型が不明なので触らせない)という波線が出て、「まず形を確認しろ」と促されます。

未経験がやりがちなミス

const data = await res.json() as any;
return data.userName; // 打ち間違えても気づけない

any にした瞬間、TypeScriptは「このオブジェクトに何があるか」を一切チェックしなくなります。

型で直す

期待する形を型(interface)で定義して、そこに合わせます。

interface User {
  id: number;
  userName: string;
}

async function getUser(): Promise<User> {
  const res = await fetch("/api/user");
  const data: unknown = await res.json();

  // 実行時に本当にその形かを確認してから User として扱う
  if (
    typeof data === "object" && data !== null &&
    "id" in data && "userName" in data
  ) {
    return data as User;
  }
  throw new Error("APIレスポンスの形が想定と違います");
}

いったん unknown で受け、in 演算子で中身を確認してから User に絞り込みます。こうすると data.userName のタイプミスは即座に波線で分かり、実行時の想定外レスポンスも throw で検知できます。

AIへの頼み方

fetch のレスポンスを any で受けないでください。
期待するレスポンスは { id: number; userName: string } です。
これを interface User として定義し、res.json() は unknown で受けてから
User かどうかを実行時にチェックして絞り込む形にしてください。
any と @ts-ignore は使わないでください。

「期待するレスポンスの形」を具体的に渡すのが最重要です。形を教えないと、AIは推測で any に逃げます。

まとめ:赤い波線への向き合い方

  • any / @ts-ignore / !エラーを消すだけで、危険は残す
  • AIへの依頼は「状況(エラー文)+期待する型+"anyやts-ignoreを使わずに"の明示」の3点セット
  • nullは絞り込み、undefined??anyなレスポンスはunknown+実行時チェック

面白いのは、この「AIに型で直させる」やり取りをしていると、TypeScriptの型の考え方そのものがAIとの対話で身についていくことです。プログラミングの知識とAI協働のスキルが同時に鍛えられる。ここは独学の型学習とは違う、AI時代ならではの学び方だと感じています。


※この記事は未経験〜駆け出し向けです。中上級の方には物足りないので、社内研修・後輩育成・知り合いへの紹介にどうぞ。
未経験から Next.js + Supabase + Claude Code でWebアプリを作って公開するまでを全20セッションで体系化した教材を作っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?