【第11回】AIの間違いを、実行する前に止める|なぜ型が要るのか(嫁子が教えるTS講座)

今回のゴール

ここから第3部(TypeScript)です。前回まで(第2部)で土台のJavaScriptは終わりました。全体の流れは講座の目次へ。


嫁子: 第2部で3回くらい「これは第3部で解決するよ」って言ったの、覚えてる?

型が混ざって静かに壊れる話と、名前を打ち間違えて undefined が返る話と、await の付け忘れね。全部ひとつの原因でつながってるの。JavaScriptは、実行するまで何も教えてくれないってこと。

今日はその答え合わせだよ。難しい文法はまだ出てこないから、気楽に読んで。


① 同じコードを並べて見る

第4回に出したものです。JavaScriptでは、これがそのまま動きます。

function total(price, count) {
  return price * count;
}

total(1200, "3");   // 3600(動いてしまう)
total("1200", 3);   // 3600(これも動く)
total(1200);        // NaN(count が無いまま計算される)

TypeScriptで型を書くと、同じコードが実行する前に止まります。

function total(price: number, count: number) {
  return price * count;
}

total(1200, "3");
// エラー: 型 'string' の引数を型 'number' のパラメーターに割り当てることはできません

total(1200);
// エラー: 2 個の引数が必要ですが、1 個指定されました

price: number の部分が型注釈です。「ここには数値が来る」と宣言しておくと、違うものが渡された時点で教えてくれます。

しかも、エディタ上で赤い波線が出ます。保存もしていない、実行もしていない段階で分かります。


② 「動かして確かめる」が減る

JavaScriptでの確認手順は、だいたいこうなります。

  1. コードを書く(または、AIに書かせる)
  2. 実行する
  3. おかしな結果が出る
  4. console.log を挟んで原因を探す

TypeScriptでは、この種の間違いが1と2のあいだで消えます。探す作業そのものが発生しません。

JavaScript TypeScript
型の取り違え 実行して初めて分かる 書いた瞬間に分かる
引数の数 足りなくても動く エラーになる
名前の打ち間違い undefined が返る エラーになる

嫁子: 3番目、地味だけど効くよ。item.nane みたいなやつね。

JavaScriptだと undefined が返るだけで、画面に何も出ないだけ。原因を探して30分、みたいなことになる。TypeScriptなら打った瞬間に赤い線が出るんだよ。


③ AIと組むと、価値が変わる

ここからが今回の本題です。型の価値は、AIにコードを書かせるようになって明確に変わりました。

AIは堂々と間違えます。第1回でも書いた通りで、存在しない機能を、あるかのように書くことがあります。

const user = getUser();
console.log(user.nickname);
// エラー: プロパティ 'nickname' は型 'User' に存在しません

このとき、そんなものは無いと即座に指摘されます。あなたが User の中身を全部覚えている必要はありません。

嫁子: これ、AIの嘘を検出する装置だと思っていいよ。

AIの文章って自然だから、読んだだけだと正しく見えるでしょ。でも型はごまかせない。存在しないものは、存在しないと出るの。

しかも見つけたエラーはそのままAIに貼ればいい。「そんなプロパティは無いって言われてる」と伝えれば直してくれるよ。人間が判断する必要すらないの。


④ 型は「AIへの仕様書」にもなる

指示を出すときにも使えます。次の2つを比べてください。

指示の書き方 AIが決めること
「ユーザー情報を受け取る関数を作って」 何を持つか、全部AIが決める
{ id: number, name: string, email: string } を受け取る関数を作って」 実装だけ

後者なら、返ってくるものの形が最初から決まっています。第5回で「データの形を先に決める」と言ったのと同じ話で、型はそれを書き留めておく場所です。

型を先に書いて、中身をAIに書かせるという進め方もできます。

// この形で作って、と型だけ渡す
type Item = {
  name: string;
  price: number;
  done: boolean;
};

function addItem(list: Item[], name: string): Item[] {
  // ここをAIに書かせる
}

嫁子: 私はこの順番がいちばん好きだな。入口と出口を人間が決めて、中はAIに任せるの。

逆にここまで決めずに丸投げすると、出てきたものを読んで判断する手間のほうが大きくなることがあるよ。


⑤ 型は実行時には消える

仕組みの話をひとつだけ。ブラウザやNode.jsはTypeScriptを直接は実行できません。

TypeScriptは、実行する前にJavaScriptへ変換されます。そのとき型注釈はすべて消えます。

// 書いたもの(TypeScript)
function total(price: number, count: number) {
  return price * count;
}
// 変換後(実際に動くJavaScript)
function total(price, count) {
  return price * count;
}

つまり型は、書いている間だけ働く見張り役です。動くもの自体は今まで通りのJavaScriptで、遅くなることもありません。

ここから2つ、大事なことが出てきます。

嫁子: 2つ目、けっこう誤解されやすいところなの。

「TypeScriptにしたから安全」ではないんだよ。守ってくれるのは自分たちが書いたコードの内部だけ。外から来るものは別途チェックが要る。そこは第5部でまた出てくるからね。


つまずきポイント

TypeScriptは別の言語ではありません。第1回で書いた通り、JavaScriptに型を足したものです。今まで書いてきたコードは、拡張子を .ts に変えればほぼそのまま動きます。

最初はエラーが増えます。それは今まで見えていなかった問題が見えるようになっただけで、コードが悪くなったわけではありません。


今回のまとめ


次にやること

次回は基本の型注釈です。今日出てきた : number のような書き方を、ひととおり読めるようにします。

今回の宿題:AIに「TypeScriptで、数値2つを受け取って合計を返す関数を書いて」と頼み、次に「引数に文字列を渡す例も見せて」と続けてください。エラーメッセージが返ってくるのを、自分の目で見ておくのが目的です。

嫁子: 型のありがたみって、説明を読むより一度止めてもらったほうが早いんだよね。

今日はまだ何も書けなくていいよ。「実行前に止まる」という感覚だけ持って次に行こう。