TypeScriptのエラーメッセージの読み方について
TypeScriptのエラーメッセージは、型が複雑になればなるほどエラー発生時にその理解を難しくするエラーメッセージとなる傾向があると思うけど、どのようにエラーメッセージを読むと理解が捗るのか確認するメモ。
エラーメッセージ
例えば、以下のような型エラーが起きるTypeScriptのコードがあった場合、
type Func = () => {
foo: {
bar: string;
baz: number;
};
};
const func: Func = () => {
return {
foo: {
bar: "baz",
baz: true, // <- numberではないので型エラーとなる
},
};
};
TypeScriptのエラーメッセージは以下のような内容で、
TS2322: Type '() => { foo: { bar: string; baz: boolean; }; }' is not assignable to type 'Func'.
Call signature return types '{ foo: { bar: string; baz: boolean; }; }' and '{ foo: { bar: string; baz: number; }; }' are incompatible.
The types of 'foo.baz' are incompatible between these types.
Type 'boolean' is not assignable to type 'number'.
4行に渡ってエラーメッセージが表示されているけども、1行ずつインデントが深くなっている。また、1行目の先頭にはTS2322
という番号が記載されている。
VSCodeのようなエディター上では変数func
に下線が引かれて、実際に問題がある箇所(foo.baz
)を把握する助けとはなりにくい。
今回の例は該当しないけども、場合によっては{ ...; }
のような形でエラーメッセージの全文が表示されずに省略されるケースもある。
エラーメッセージの構成
このエラーメッセージの内容がどのように構成されているのか。
TypeScript: Documentation - Understanding Errorsでは、以下のように記述があり、
Each error starts with a leading message, sometimes followed by more sub-messages. You can think of each sub-message as answering a “why?” question about the message above it. Let’s work through some examples to see how they work in practice.
先頭の1行のエラーメッセージから始まり、それ以降のサブメッセージの各行では前行の「なぜ?」を説明する内容となることが分かる。上述の例であれば:、
-
() => { foo: { bar: string; baz: boolean; }; }
となる値をFunc
型に割り当てられない
TS2322: Type '() => { foo: { bar: string; baz: boolean; }; }' is not assignable to type 'Func'.
-
Func
型に割り当てられないのはなぜか。それは、関数の返り値の型とCall signatureの返り値の型が一致しないから。
Call signature return types '{ foo: { bar: string; baz: boolean; }; }' and '{ foo: { bar: string; baz: number; }; }' are incompatible.
- 関数の返り値の型とCall signatureの返り値の型が一致しないのはなぜか。それは、それぞれの
foo.baz
プロパティの型が一致しないから。
The types of 'foo.baz' are incompatible between these types.
- それぞれの
foo.baz
プロパティの型が一致しないのははぜか。number
型へboolean
型が割り当てられないから。
Type 'boolean' is not assignable to type 'number'.(2322)
というように、1つずつエラーの内容を掘り下げていく形になっていることがわかる。
なお、2322
は、エラーの診断コードにあたる。Coding guidelines · microsoft/TypeScript Wiki · GitHubに1000番台から7000番台まではカテゴリーの説明がある。
2000番台はセマンティックなものであり、上述の型が割り当てできない2322
のケースなど構文的には問題ないけども型としてはエラーになるものが多いと思われる。
エラーメッセージを理解しやすくするには?
Structure of a TypeScript Error | Total TypeScriptによれば、関数自体の型をCall signatureで型定義するのではなく、返り値やパラメータ等をそれぞれ個別に型定義するのが、わかりやすいエラーメッセージを出す観点では良さそう。
Objects are less complex than functions (they can't be overloaded), so TypeScript can actually dispense with a long error and show only the line that matters:
...
So, if you're looking to improve your TypeScript errors - aim to always compare objects to objects, instead of functions to functions. This means you should prefer typing return types and parameters over giving a type to the function itself.
例として利用しているTypeScriptのコードを以下のように返り値の型定義に変えると
type FuncReturnType = {
foo: {
bar: string;
baz: number;
};
};
const func = (): FuncReturnType => {
return {
foo: {
bar: "str",
baz: true,
},
};
};
エラーメッセージは以下の1文のみに変化して、VScodeのようなエディター上ではfoo.baz
の箇所に下線が引かれるので、どこに問題があるのか一目で把握できるようになる。
TS2322: Type 'boolean' is not assignable to type 'number'.
VSCodeの拡張機能
VSCodeであれば、人間が読めるエラーメッセージにする拡張機能としてpretty-ts-errorsがある。
この拡張機能は、TypeScript Error Translatorによって平易な英語でエラーを説明してくれる機能を持つ。英語以外の言語への対応についてはSupport more languages and locales · Issue #27 · yoavbls/pretty-ts-errorsをウォッチしておくと良いかもしれない。
参考
- TypeScript: Documentation - Understanding Errors
- Structure of a TypeScript Error | Total TypeScript
- How to read TypeScript errors
- Deciphering TypeScript’s React errors | by Fiona Hopkins | Innovation and Technology | Medium
- Interpreting Errors - TypeScript Deep Dive
- react native - How to read typescript error in order to understand what type should be used? - Stack Overflow
- Coding guidelines · microsoft/TypeScript Wiki
- typescript - Difference between call signature and function type - Stack Overflow
Discussion