TypeScriptのエラーを直して覚える
実際に出力されるメッセージを読みながら、空欄を埋めて直します。全102問。間違えた問題は翌日・3日後・7日後に自動で再出題されます。
エラー別に調べる
初級
- is not assignable to type の原因と直し方変数への代入・関数の戻り値・プロパティへの代入など、宣言した型と違う型の値を渡そうとしたときにtscが検出するエラーです。TypeScriptで最初に出会うエラーの1つです。
- implicitly has an 'any' type の原因と直し方関数のパラメータに型注釈を付けないと、strictモードのTypeScriptは型を推論できず「暗黙のany」としてエラーにします。anyは型チェックを無効化してしまうため、TypeScriptの利点を守るために既定で禁止されています。
- Cannot find name の原因と直し方宣言されていない変数・関数名を参照したときに出るエラーです。TypeScriptは近い名前が見つかると「Did you mean 'x'?」という候補も一緒に表示してくれます。
- Expected N arguments, but got M の原因と直し方関数に定義されているパラメータの数と違う数の引数を渡して呼び出したときに出るエラーです。JavaScriptでは引数が多すぎても少なすぎても実行できてしまいますが、TypeScriptは呼び出し時点で検出します。
- 引数の型が合わないエラーの原因と直し方関数やメソッドの引数に、パラメータの型と違う型の値を渡したときに出るエラーです。配列のpush()のような組み込みメソッドの引数でも同じ形式のエラーになります。
- 必須プロパティの書き忘れエラーの原因と直し方interfaceで定義した必須プロパティを、オブジェクトリテラルで指定し忘れたときに出るエラーです。1つでも足りないプロパティがあれば、その型の値として代入できません。
- 未定義プロパティを書いたエラーの原因と直し方interfaceに定義されていないプロパティを、オブジェクトリテラルに書いてしまったときに出るエラーです。プロパティ名のタイプミスもこの形で検出されます。
- Property does not exist on type の原因と直し方型に定義されていないプロパティ名でアクセスしようとしたときに出るエラーです。プロパティ名のタイプミスが典型的な原因で、近い名前があれば候補も表示されます。
- Cannot assign to because it is a constant の原因と直し方constで宣言した変数に再代入しようとしたときに出るエラーです。JavaScriptでは実行時のTypeErrorでしたが、TypeScriptは実行前のコンパイル段階でこれを検出します。
- 非nullアサーション(!)がコンパイラを騙して実行時エラーになる変数名の後ろに!を付ける非nullアサーションは、「この値はnull/undefinedではない」とコンパイラに約束する構文です。実際にはundefinedだった場合、型チェックは通過しますが実行時に例外が発生します。
- 配列の範囲外アクセスがエラーにならずundefinedになる(TypeScriptでも変わらない)TypeScriptは配列の要素数を型では追跡しないため、number[]のような型でも範囲外アクセスはコンパイルエラーになりません。実行時もJavaScriptと同じく、静かにundefinedを返します。
- 文字列メソッドは元の文字列を変更せず新しい文字列を返すtoUpperCase()のような文字列メソッドは、呼び出し元の文字列そのものを書き換えるのではなく、変換後の新しい文字列を戻り値として返します。戻り値を使わずに呼び出しただけでは何も変わりません。
- Array.prototype.sort()が数値を文字列として並べ替える理由比較関数を渡さないsort()は、number[]という型が付いていても要素を文字列に変換してUnicodeコードポイント順に並べ替えます。数値の大小とは無関係な結果になります。
- 文字列と数値の+の左結合で意図しない連結になる文字列と数値を+で連続してつなげると、最初の+で文字列に変換され、それ以降の+はすべて文字列連結として扱われます。型注釈でnumberと宣言していても、+演算子自体の挙動はJavaScriptと同じです。
中級
- is possibly 'undefined' の原因と直し方interfaceで?を付けたオプショナルプロパティは、値が入っていない(undefined)可能性を型として保持します。narrowing(存在確認)をせずにメソッドやプロパティへアクセスすると、このエラーになります。
- union型(string | number)のメソッド呼び出しで型エラーになる理由string | numberのようなunion型の値に対して、片方の型にしか無いメソッド(toUpperCaseなど)を呼び出そうとすると、もう一方の型に無いことを理由にエラーになります。typeof等で型を絞り込む(narrowing)必要があります。
- incorrectly implements interface の原因と直し方クラスがimplementsで宣言したinterfaceのメンバー(メソッドやプロパティ)を実装し忘れたときに出るエラーです。interfaceは「このクラスが持つべき形」の契約であり、implementsはその契約を守る宣言です。
- 文字列リテラルをenum型の引数に渡すとエラーになる理由enumで定義した型は、見た目が近い文字列リテラルであっても別の型として扱われます。enumのメンバー名と同じ文字列を渡しても、enum型そのものの値でなければ代入・引数として受け付けられません。
- readonly配列にpushしようとするとdoes not exist on typeになる理由readonly number[]のように宣言した配列型には、push・pop・splice等の破壊的(要素を変更する)メソッドがそもそも型として存在しません。呼び出そうとした時点で「そのメソッドは無い」というエラーになります。
- abstractクラスを直接newした原因と直し方abstractを付けたクラスは、そのままnewでインスタンス化することができません。abstractクラスは「共通の骨組みだけを定義し、具体的な実装はサブクラスに任せる」ためのクラスです。
- ジェネリクスの型引数を明示すると戻り値の型チェックが効くfunction firstOf<T>(items: T[]): Tのようなジェネリック関数は、呼び出し時に指定した型引数(例: firstOf<number>)に応じて戻り値の型が決まります。その型と違う型の変数で受け取ろうとするとエラーになります。
- No overload matches this call の原因と直し方複数のシグネチャ(オーバーロード)を持つ関数を、どの組み合わせにも当てはまらない引数の型で呼び出したときに出るエラーです。エラーメッセージには、試したすべてのオーバーロードでの失敗理由が列挙されます。
- asによる型アサーションがコンパイラを騙して実行時エラーになるasはコンパイラに「この値はこの型として扱ってよい」と伝える構文で、実際の値を変換したり検証したりはしません。unknown型の値をasで別の型に見せかけると、実際の値がその型のメソッドを持たない場合に実行時エラーになります。
- switchで一部returnを書き忘れた原因と直し方union型の値をswitch文で分岐する関数で、一部のケースにしかreturnを書かないと、「関数がreturn文で終わっていない」というエラーになります。TypeScriptはunion型の全パターンをたどって、返し忘れがないかを検査します。
- タプル型は要素数も型として扱われる(余分な要素はエラーになる)[number, number]のようなタプル型は、要素の型だけでなく要素数も型情報の一部です。通常のnumber[]と違い、決められた数より多い(または少ない)要素を持つ配列は代入できません。
- インデックスシグネチャと矛盾する型のプロパティを宣言するとエラーになる[key: string]: numberのようなインデックスシグネチャを持つinterfaceでは、個別に宣言する具体的なプロパティも、インデックスシグネチャの型と互換性がなければ宣言できません。
上級
- Mapped typeも通常と同じ型チェックを受ける理由{ [K in keyof T]?: T[K] }のようなmapped typeで生成した型は、見た目は特殊でも、実体は通常のinterfaceと同じようにプロパティごとの型チェックが行われます。生成された型だからといって検査が緩くなることはありません。
- keyof typeofで作ったキーの型は、存在しないキーを渡すとエラーになるkeyof typeof objは、実行時のオブジェクトobjが実際に持つキーだけを表すunion型(リテラル型の集合)になります。定義時に存在しないキー文字列を渡すと、その型に含まれないためエラーになります。
- ジェネリクスのextends制約を満たさない型を渡すとエラーになる<T extends { length: number }>のように書くと、Tには「lengthプロパティを持つ型」しか渡せなくなります。制約を満たさない型(例えばnumber)を渡すと、呼び出し時点でエラーになります。
- 型注釈(: Type)はプロパティを消さずに「見えなく」する。satisfiesとの違い変数に: Typeという型注釈を付けると、その型に無いプロパティへのアクセスができなくなります。実行時にはプロパティ自体は残っていますが、型の上では「そんなものは無い」ものとして扱われます。satisfiesは元の値の型をそのまま保つため、この制限を受けません。
- 同名interfaceのマージで型が矛盾した原因と直し方同じ名前のinterfaceを複数回宣言すると、TypeScriptはそれらを自動的に1つに合成します(宣言のマージ)。ただし同じプロパティ名を違う型で宣言すると、合成できずにエラーになります。
- 変数経由なら余分なプロパティが通る理由オブジェクトリテラルを直接関数に渡すと余分なプロパティはエラーになりますが、いったん変数に代入してから渡すとエラーになりません。TypeScriptは「作られたばかり(フレッシュ)」なリテラルだけを厳格にチェックする設計になっています。
- as unknown as Xの二重キャストは型チェックを完全に迂回し、実行時エラーの原因になるTypeScriptは通常、明らかに無関係な型同士のasによるキャストをエラーにします。しかしunknownを経由する(as unknown as X)と、この安全装置を完全に迂回でき、どんな型にも変換できてしまいます。
- 数値enumはObject.keys()で要素数が2倍になる(逆引きマッピングの副作用)数値enumは、コンパイル後のJavaScriptオブジェクトに「メンバー名→値」だけでなく「値→メンバー名」の逆引きも自動的に持たせます。この副作用でObject.keys()を呼ぶと、メンバーの2倍の数のキーが返ってきます。