型エラーは「TypeScriptからの手紙」だと思って読む
まず前提として、型エラーは「お前のコードがダメ」と責めてくるものじゃなくて、
「ここがこうズレてるよ」と教えてくれるかなり親切な手紙です。
だから読むときは、
- どの行で
- 何が期待されていて
- 実際に何が来てしまっていて
- そのズレをどう直せばよさそうか
この4つを落ち着いて拾っていくイメージで見ていきます。
型エラーの基本構造を押さえる
典型的なエラーメッセージの形
よく見るエラーをひとつ出してみます。
let age: number = 20;
age = "30";
TypeScriptこれをコンパイルすると、だいたいこんなエラーが出ます(IDEならホバーでも見える)。
Type '"30"' is not assignable to type 'number'.
TypeScriptここで読み取るべきポイントはシンプルです。
Type '"30"'…「実際に来ている型」is not assignable to type 'number'…「期待されている型」と「代入できない」という宣言
つまり、「"30"(string)は number には代入できませんよ」という意味です。
TypeScript公式ドキュメントでも、「assignable to は“代入可能かどうか”を表すキーワード」と説明されています。
「expected(期待される型)」と「actual(実際の型)」を見分ける
どっちが悪いのかを見極める
もう少し複雑な例を見てみます。
function greet(name: string) {
console.log(`Hello, ${name}`);
}
greet(123);
TypeScriptエラーはこうなります。
Argument of type 'number' is not assignable to parameter of type 'string'.
ここでも構造は同じです。
Argument of type 'number'…「実際に渡しているのは number」parameter of type 'string'…「関数側は string を期待している」
つまり、「呼び出し側(greet(123))が悪い」のか、「関数定義側(name: string)が間違っている」のかを考える必要があります。
多くの場合は「引数の型を直す」か「関数の型定義を見直す」かのどちらかです。
よく見るキーワードの意味を知っておく
assignable to(代入可能かどうか)
さっきも出てきた is not assignable to は、TypeScriptのエラーで頻出ワードです。
Type 'A' is not assignable to type 'B'.
→「A 型の値は B 型の場所には置けません」
これは「代入」だけでなく、関数呼び出しや戻り値、オブジェクトのプロパティなど、
“ある型の値を、別の型が期待されている場所に持っていったとき”に広く使われます。
Property ‘xxx’ does not exist on type ‘YYY’
const user = { name: "Taro" };
console.log(user.age);
TypeScriptこのときのエラーはだいたいこうです。
Property 'age' does not exist on type '{ name: string; }'.
意味はそのまま、「age なんてプロパティ、この型にはないよ」です。
「オブジェクトの形(型)に、そのプロパティが定義されているか?」を確認するサインだと思ってください。
Object is possibly ‘null’ / ‘undefined’
let name: string | null = null;
console.log(name.toUpperCase());
TypeScriptこのときはこう怒られます。
Object is possibly 'null'.
「そのオブジェクト、null かもしれないのにメソッド呼んでない?」という警告です。if (name !== null) で絞り込むか、設計として本当に null を許すのかを見直すポイントになります。
エラーを読むときの「視線の動かし方」
1. まず「どの行か」を見る
エディタやコンパイラは、エラーが起きた行番号を必ず出してくれます。
まずはそこにジャンプして、「どのコードが問題になっているか」を目で確認します。
2. 次に「期待されている型」と「実際の型」を読む
エラーメッセージの中から、
type 'XXX'(実際の型)type 'YYY'(期待されている型)
の2つを探します。XXX is not assignable to YYY の形になっていることが多いので、「XXX を YYY の場所に置こうとして怒られている」と理解します。
3. 最後に「どっちを直すべきか」を考える
- 変数の型注釈が間違っているのか
- 代入している値の方が間違っているのか
- 関数の引数定義を変えるべきなのか
- 呼び出し側の使い方を変えるべきなのか
ここは設計の話なので、「どちらが正しい世界観か」を自分で決める必要があります。
エラーは「ズレ」を教えてくれるだけで、「どちらが正解か」までは決めてくれません。
具体例で「読み方の流れ」を体に入れる
例1:ユニオン型との不一致
type Status = "success" | "error";
let s: Status = "success";
s = "pending";
TypeScriptエラー:
Type '"pending"' is not assignable to type '"success" | "error"'.
読むポイントはこうです。
- 実際の型:
"pending" - 期待される型:
"success" | "error" - 結論:「Status 型は success / error しか許していないのに、pending を入れようとしている」
ここでやるべきことは、
- 本当に pending を許したいなら、
type Status = "success" | "error" | "pending";に直す - そうでないなら、代入している
"pending"側を修正する
のどちらかです。
例2:オブジェクトの形が合っていない
type User = {
id: number;
name: string;
};
const u: User = {
id: 1,
// name がない
};
TypeScriptエラーはだいたいこうなります。
Property 'name' is missing in type '{ id: number; }' but required in type 'User'.
読むポイントは、
- 「
{ id: number; }というオブジェクトを User 型として扱おうとしている」 - 「でも User 型には name が必須なのに、それが missing(足りない)」
ということ。
つまり、「User の定義を変えるか」「オブジェクト側に name を足すか」を選ぶ場面です。
「エラーを怖がらない」ためのマインドセット
型エラーは、慣れるまではどうしても「英語の長文に怒られている」感が出ます。
でも、構造さえ分かれば、実は言っていることはかなり単純です。
- どの行で
- どの型とどの型がぶつかっていて
- どっちが期待されていて
- どっちが場違いなのか
これだけ拾えれば十分です。
最初は、出てきたエラーをスクショでもメモでもいいから残しておいて、
「このエラーはこういう意味だった」と自分なりに日本語に訳していくと、
だんだんパターン認識できるようになります。
