🐷

TypeScriptのエラーメッセージの読み方について

2023/06/18に公開

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行のエラーメッセージから始まり、それ以降のサブメッセージの各行では前行の「なぜ?」を説明する内容となることが分かる。上述の例であれば:、

  1. () => { foo: { bar: string; baz: boolean; }; }となる値をFunc型に割り当てられない
TS2322: Type '() => { foo: { bar: string; baz: boolean; }; }' is not assignable to type 'Func'.
  1. Func型に割り当てられないのはなぜか。それは、関数の返り値の型とCall signatureの返り値の型が一致しないから。

  Call signature return types '{ foo: { bar: string; baz: boolean; }; }' and '{ foo: { bar: string; baz: number; }; }' are incompatible.

  1. 関数の返り値の型とCall signatureの返り値の型が一致しないのはなぜか。それは、それぞれのfoo.bazプロパティの型が一致しないから。

    The types of 'foo.baz' are incompatible between these types.

  1. それぞれの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がある。

https://twitter.com/t3dotgg/status/1647759462709747713?cxt=HHwWgoDToYmggt4tAAAA

この拡張機能は、TypeScript Error Translatorによって平易な英語でエラーを説明してくれる機能を持つ。英語以外の言語への対応についてはSupport more languages and locales · Issue #27 · yoavbls/pretty-ts-errorsをウォッチしておくと良いかもしれない。

参考

GitHubで編集を提案

Discussion