TypeScriptのObject.keys又はObject.valuesをfind関数の後に使ったら怒られたのでその対処法


find関数を使って取得したオブジェクトのキー名が不明だったためObject.valuesを使って目的の値を取得しようとしたところ型が違う（オーバーロード）とエラーが表示されてしまったので記事にしました。

## Object.values使おうとしたらエラーが起こったところ

以下のようにObjの値を取り出そうとした中で起こりました。

```test.tsx
    const stuffVolFind = stuff.StuffVolumeArray.find(
        (item) => item[material] !== undefined
      );
    const stuffVol = Object.values(stuffVolFind)[0];
```
<b>stuffVolFind</b>は配列の中から結果としてオブジェクトを１つだけとってきます。オブジェクトの型には、

```test.d.ts
interface StuffVolumeArrayType { [keyName: string]: number; }
```
が入っているためキーは変数名となっておりkey名で直接、値を取得することはできません。

<b>stuffVol</b>ではオブジェクトが１つしかないためfind関数で取得した配列の[0]を指定しています。

しかしこのままでは以下のエラーが stuffVol = Object.values(stuffVolFind)の "stuffVolFind"に対して生じました。

```
No overload matches this call.
  Overload 1 of 2, '(o: { [s: string]: number; } | ArrayLike<number>): number[]', gave the following error.
    Argument of type 'StuffVolumeArrayType | undefined' is not assignable to parameter of type '{ [s: string]: number; } | ArrayLike<number>'.
      Type 'undefined' is not assignable to type '{ [s: string]: number; } | ArrayLike<number>'.
  Overload 2 of 2, '(o: {}): any[]', gave the following error.
    Argument of type 'StuffVolumeArrayType | undefined' is not assignable to parameter of type '{}'.
      Type 'undefined' is not assignable to type '{}'.
```
２つのオーバーロード（型違い）でできませんよと怒られます。

## 対策（ダメだった例）

エラーについて、正直なところTypeScriptのObjectの仕様のところの指摘だったので時間がかかりそうだったのでこちら側で型そのものを強制的に変えることにしました（本当はよくないけれど）

```test.tsx
    const stuffVolFind = stuff.StuffVolumeArray.find(
        (item) => item[material] !== undefined
      );
    const stuffVolObj = stuffVolFind as Object;
    const stuffVol = Object.values(stuffVolObj)[0];
```
as Object の型アサーションを用いて強制的にObject型に上書きさせた。
こうすることでObject.valuesとObject.keysの両方で試したところ通すことはできました。

しかし残念ながらESLintには怒られます。

```
Don't use `Object` as a type. The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`.
```
anyと同等だよということですね。

## 対策（ベターで採用した例）
エラーの原因は find関数 で返ってくる型に " ObjectのType名 | undefined " のように " undefined " がついてくるところが原因だった。

undefined入りのObjectを Object.keys又はObject.values に渡すことはできません。

しかし今回のコードでは find関数 が必ず返ってくることは分かっているため " ObjectのType名 " のみを型アサーションを用いて上書きすることにしました。

```test.tsx
    const stuffVolFind = stuff.StuffVolumeArray.find(
        (item) => item[material] !== undefined
      );
    const stuffVolObj = stuffVolFind as StuffVolumeArrayType;
    const stuffVol = Object.values(stuffVolObj)[0];
```

StuffVolumeArrayTypeとは ObjetのType名 です。
そうすることで undefined の型はなくなるので


## もっと良い方法はあるとは思う

おそらくモアベターな方法としては TypeScript側で定められたObjectの型 を拡張するやり方。
少し調べてみたところジェネリクスを用いた方法は見つかったのだけど理解がもう少し必要そうであったため時間との兼ね合いで今回はスルーしました。
（分かったら追記します）

型アサーション( asを用いた上書き手法 )は強制上書きでありなるべく使わない方が良いためです。
誰か拡張の仕方がわかる方、教えてもらえると嬉しいです。

## 追記（コメントで教えてもらった手法）
(@sdkei さんからコメントもらいこちらの方が良くてこっちを取り入れたので追記という形で紹介させていただきました)

if (stuffVolFind === undefined) {例外処理} で事前に undefined の場合の処理を挟むことで undefinedの可能性を消すことができました。

```test.tsx
    const stuffVolFind = stuff.StuffVolumeArray.find(
        (item) => item[material] !== undefined
      );
    if (stuffVolFind === undefined) {
      throw new Error("stuffVolFind の値が undefined でした。");
    }
    const stuffVol = Object.values(stuffVolFind)[0];
```
型アサーションも不要だしシンプルに使えるのでこの方法はもっと使っていきたい所存。