スタックトレースは、エラーが起きるまでに通った関数の履歴です。上の行は例外が起きた場所、下の行ほどその関数を呼び出した場所を示します。
今回やること
TypeError: Cannot read properties of undefined
at formatUser (src/user.js:18:20)
at renderProfile (src/profile.js:42:12)
at async loadPage (src/page.js:9:3)

この例では、formatUserの18行目で未定義の値を使っています。しかし直す前に、renderProfileが何を渡し、loadPageがどんな応答を受け取ったかを確認します。
読む順番
- エラー名とメッセージから、何ができなかったかを読む
- 最初の自分のファイルを開き、どの式が失敗したか見る
- 一つ下のフレームで、その値を渡した場所を見る
- 入力値やAPI応答の生成元までさかのぼる
例えば次のコードなら、user.nameを読む場所が失敗地点です。
function formatUser(user) {
return user.name.toUpperCase();
}
userがなぜ未定義なのかは、この関数だけでは分かりません。呼び出し元で受け取ったAPI応答、URLのID、対象が見つからない時の仕様を順に確認します。
ライブラリの行が多い時
node_modulesの行が並んでも、まず自分のファイルを探します。ライブラリの不具合と決めつけず、渡した引数や使い方がAPIの契約に合っているかを確認してください。
非同期処理では途中のフレームが省略されることがあります。Request ID、Networkの応答、例外直前のログを同じリクエストとして結びます。Error.stackの形式はJavaScriptの標準仕様ではないため、ブラウザごとの差もあります。
成功確認
次のメモを埋められれば、調査の出発点を特定できています。
例外が起きた式:
呼び出し元:
渡された値:
その値が作られた場所:
通常時と異なる入力:
修正後は、問題の入力だけでなく、通常の入力、対象がない入力、通信失敗の入力を試します。例外が消えただけでなく、各状態が仕様どおり表示されることを確認します。
よくあるつまずき
最上段にnullチェックを足して終える
画面は落ちなくなっても、APIが対象なしを誤って成功として返しているかもしれません。未定義になった理由を追ってから、防御処理を置く境界を決めます。
本番の行番号をそのまま元コードへ当てる
圧縮済みのコードは行番号が変わります。source mapが同じリリースに対応するか確認し、必要ならDevToolsの例外ブレークポイントで停止します。
練習
amount.toFixed is not a functionが出た時、失敗地点、呼び出し元、API応答でそれぞれ何を確認するかを書いてください。amountが文字列だった場合、変換する責任をどこへ置くかも考えます。