TypeError: Cannot read properties of undefined (reading '***')エラーを解決してみた

## 背景

業務ではまったエラーです。
個人的には、`TypeError: Cannot read properties of undefined (reading '***')`というエラーは自分のような経験の浅い人にとっては原因を見つけづらいエラーの一つだと思い、今回記事にまとめました。
*コードはフェイクですので少しわかりづらいかも。

原因から考えると解決方法は複数ありそうですが、一つの参考になれば嬉しいです。

## 前提(今回のストーリー)

例えばユーザー登録されたユーザーが複数いて、admin権限を持つユーザーが一人だけという内容の以下のデータがあり、このデータをAPIで取得しているとする。
```javascript
// APIにてusersを取得したとする
users = [
  { id: 1,
    userName: 'suzuki',
    admin: true
  },
  { id: 2,
    userName: 'tanaka',
    admin: false
  },
  { id: 3,
    userName: 'sasaki',
    admin: false
  },
]
```

上記の中のadmin権限を持つユーザー情報だけを表示したいときに、以下の状態だとエラーが出る。
```javascript

export const App = () => {

// この時点でusersは空配列
  let users = []

  // ①この時点でユーザーデータ取得のAPIが走る(useEffectを用いたdispatch)

  // adminUserを抽出
  const adminUser = users.filter(user => {
    return user.admin === true
  })

  // 一度目のレンダリング(この時点では①の取得が完了していない)
  // ↓↓の時点でエラーが発生
  const adminUserDisplay = () => {
    return adminUser[0].userName
  }

　 　// ②APIによるユーザーデータ取得完了した時点で、このタイミングでadminUserDisplayを実行したいが、上記エラーで止まってしまっているため実行できない。

  return (
    <>
      <div className="App">
        <h1>adminUserを表示</h1>
        <div>{adminUserDisplay()}</div>
      </div>
    </>
  );
}

```
少しわかりづらいかもしれないが、上記のようにadminUserを取得する処理が上記コンポーネントが描画されるまでに間に合わず、`users`が空になっていたことで今回のエラー`TypeError: Cannot read properties of undefined (reading '***')`が発生した。
直訳すると、「タイプエラー: 未定義のプロパティは読み込めません。」
*タイプエラーのタイプは型という意味

```javascript
// console.logでadminUserを出力すると空配列が出力される
console.log(adminUser)
// >> []
```

## 解決方法

上記の場合、usersデータを取得できる前とできた後で２回レンダリングされる。
取得できる前にエラーが発生してしまい、２回目のレンダリングが走らない状況だった。

なので、１回目のエラー回避をしてあげれば２回目のレンダリングまでたどり着けるはず。ということでadminUserDisplayメソッドに以下の条件を追加することで解決できた。

```javascript
if (typeof adminUser[0] !== 'undefined') {
 return <div>{currentMainUser[0].id}</div>
```

## typeof演算子（undefined判定）

`if (typeof adminUser[0] !== 'undefined')｀
要はadminUser[0]がundefinedじゃない場合にreturnするという簡単な条件。
単純に`adminUser[0] !== 'undefined'`だと以下のエラーとなる。
```javascript
if (t === 'undefined') {
  console.log('t is undifined!!')
} 
// >> Error: t is not defined
```
undefinedは'undefined'だとundefindだと認識してくれない。
伝わるだろうか？'undefined'は文字列のundefinedとして認識されるので、状態としてのundefinedではない。

### [Primitive（プリミティブ）](https://developer.mozilla.org/ja/docs/Glossary/Primitive)

- `undefined`のようにメソッドを持たないデータのことを`プリミティブ`と呼ぶそうです。
- 文字列、数値、BigInt、真偽値、undefined、シンボルの６種類
- プリミティブは全てイミュータブル（変更不可）
→つまり、consoleやエラーで吐かれる`undefined`は文字列としてのundefinedではないんですね。

そこで、[typeof](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/typeof)を使うことでundefined状態なのかを判定できる。

```javascript
if (typeof t === 'undefined') {
  console.log('t is undefined!!')
} 

// >> "t is undefined!!"
```

`typeof`演算子の後に変数を置くことで、変数の型を返してくれる。
*変数だけじゃなく、数値、文字列でも型を返してくれる
```javascript
console.log(typeof t)
// >> "undefined"

console.log(typeof 10)
// >> "number"

console.log(typeof "hoge")
// >> > "string"
```

## 懸念点

一旦これでエラーを回避することはできた。
しかし、xがnull, falseなどの`typeof x === 'undefinded'`が好ましくない場合もあるので注意が必要。



## 学び

- `TypeError: Cannot read properties of undefined (reading '***')`の難しさはなぜundefinedになっているかを考える必要がある部分にある
- undefinedの判定にはtypeofを使う必要がある
- null, falseなどの値をとりうるのか、など考えることが増えた


## 参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/undefined

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/typeof