Anyダメ絶対! axios編

ウェブクルー AdventCalendar 2日目の記事です。
今年もアドベントカレンダーの時期がやってきました!


**※タイトルはオーバーな表現が含まれておりますがこちらは演出によるものです予めご了承ください**

# About me
@kouchanne はウェブクルーに2017年に**新卒**で入社した、今年4年目になるエンジニアです。
**新卒**を売りにしてますが、4年も立つと新卒ブランドはすっかりなくなってしまうものですね...
それはさておき、今年も元気に記事を書いて行こうと思いますのでどうぞお付き合いくださいm(_ _)m

# 序章
最近弊社でも、Vue.js(Nuxt.js)が導入されてきたので、RESTAPIを叩いてあーだこーだするみたいなケースが増えてきました。
※Nuxt.jsの導入で頑張った話は[こちら](https://note.com/kouchanne/n/nb656bd2d3773)でまとめてますのでよろしければご覧ください。

基本的にVue.jsを書く際はTypeScriptを利用するようにしているんですが、TypeScriptをやっていく上で絶対ぶつかる壁がありますよね...

そう **Any** こいつです

![スクリーンショット 2020-11-25 18.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206268/9006d7dd-c6f6-87b4-549e-59ee04538e65.png)

TypeScript対応していない、JavaScript製のライブラリを使っている際に出てくるAnyはある程度しょうがない部分もありますが、自分で定義するものに関してはできる限りAnyを避けることでTypeScriptの恩恵を得たいですよね。

今回は、axiosにフォーカスしてTypeScriptの恩恵をできる限る受ける方法をご紹介したいと思います。

# そもそも [Any](https://www.typescriptlang.org/docs/handbook/basic-types.html#any) is 何
そもそもTypeScriptというのはJavaScriptに型情報を持たせることで、実装時に意図しない値になることを未然に防ぐものです。

JavaScriptはもともと予めすべての型情報を確定させなくてはならないという制約がないため、型情報を予め定義していないものに関しては、どうしてもどの型にも当てはめることができないという状態が発生してしまいます。

しかし、それでは実装時に型を確定させる必要があるTypeScriptでは実装ができなくなってしまいます、
そこで登場するのが **「とりあえず、何くるかわからんけどOK」** というのがAnyになります。


# axiosの場合
**「でも叩くAPIは決まっているし、型は確定しているんじゃないの？」**

って思う方もいらっしゃると思います、
しかし、RESTAPIでやり取りする値はJSONというフォーマットになります。

JSONは

```json
{
 "hoge": "hogehoge",
 "fuga": [
  {
    "fuga": "fugafuga"
  }
 ]
}
```

こんな見た目をしているので、一見JSONそのものがオブジェクトという錯覚をしてしまうのですが正確には
JSONフォーマットの文字列(string)をJavaScript側で変換してオブジェクトにするという流れを経ています

その際、ただの文字列情報に型情報を持つことができないので、最終的にやってくる値がAnyになってしまうというわけです。

しかし、axiosでは事前に型をセットすればResponseを特定の型にすることができるのでそちらをご紹介します。


# やってみる
今回のサンプルプログラムは[こちら](https://github.com/kouchanne/typescript-sample)のリポジトリにあります

## 想定するAPI
今回叩くAPIから返ってくるResponseとして以下のようなデータが返ってくることを想定して実装します

### 成功パターン

```json
{
    "status": "SUCCESS",
    "results": [
      {
        "id": 1,
        "name": "サンプル タロウ",
        "tel": "電話番号が入ります",
        "address": "住所が入ります",
        "age": 18
      },
      {
        "id": 2,
        "name": "サンプル タロウ",
        "tel": "電話番号が入ります",
        "address": "住所が入ります",
        "age": 56
      },
      {
        "id": 3,
        "name": "サンプル タロウ",
        "tel": "電話番号が入ります",
        "address": "住所が入ります",
        "age": "ほげほげ"
      }
    ]
  }
```

### 失敗パターン
```json
{
  "status": "FAILED"
  "message": "データの取得に失敗しました"
}
```

## 1. まずは必要な型を作成する
上で想定している成功パターンと失敗パターンに合わせて、型を作成します。

### 成功パターン
成功時は `SuccessResponse` という型を作成してみましょう

```typescript
interface SuccessResponse {
  status: 'SUCCESS'
  results: User[]
}
```

`User` データは単体で使い回すことが想定されるので、別途で `User` 型として定義するのが良いでしょう。

次のような形で定義します。

```typescript
type User = {
  id: number,
  name: string,
  tel: string,
  address: string,
  age: number
}
```

これで、User情報の部分の型を作成することができました。
この型を `results` の部分に配列としてセットします!

ちなみに、今回 `Interface` と `Type Alias` どちらも使っていますが、大体できることは一緒です。
この2つの使い分けに関しては、[この方の記事](https://qiita.com/SotaSuzuki/items/efe32e07e52dce329653)がわかりやすかったので参考にしてみてください


### 失敗パターン
次に失敗パターンの型を作成します
`FaiedResopnse` という型で定義します。

```typescript
interface FaiedResopnse {
  status: 'FAILED'
  message: string
}
```

実際のAPIはもうちょっと情報が多いと思いますが、今回はサンプルなのでシンプルに書いてます。

ちなみに、 `status` の部分がstringではなく文字列になっていますが、これは[リテラル型](https://typescript-jp.gitbook.io/deep-dive/type-system/literal-types)といって、その文字列のみを許可する型を定義することができます。


## 2.実際にaxiosに作成した型を当てはめる
先程作成した形をaxiosのResponseの成功パターンと失敗パターンそれぞれで使えるようにします。

やり方は簡単です、先程定義した型を
成功パターンを `get` の型にセット
失敗パターンを `AxiosError<FaiedResopnse>` としてerrの型にセットするだけです。

```typescript
import axios,{AxiosError} from 'axios'

const getUsers = () => {
  return axios.get<SuccessResponse>('http://localhost:3000/users').then((res) => {
    const data: SuccessResponse = res.data  /* dataの型がSuccessResponseになってる */
    return data
  }).catch((err: AxiosError<FaiedResopnse>) => {
    const data: FaiedResopnse = err.response?.data ?? {status: 'FAILED', message: err.message}        /* dataの型がFaiedResopnseになってる */
    return data
  })
}
```

なぜエラー時だけ `AxiosError` をがあるかというと、型をセットするタイミングが異なるためです
axiosの `get` に渡すことで予めresponseデータに型をセットした状態で取得ができるようになります
なので、以下のようにも書けると思います

```typescript
axios.get('http://localhost:3000/users').then((res: AxiosResponse<SuccessResponse>)
```




# 終了
必要な工程はこれだけです！
これで、axiosのResponseに型をセットすることができるようになりました。

ちなみに、[サンプル](https://github.com/kouchanne/typescript-sample/blob/main/src/index.ts#L27)ではこれから更にJSONのバリデーションをかけて失敗した場合エラーにするという処理も入れています。
まぁ意図しないところでエラーになっちゃうと困るので、あまり出番はないかもですが、こうすることでありえない値を呼び出そうとして実行時エラーになるということを未然に防ぐことができるようになるのでより型安全なTypeScriptにすることができるようになります。

# まとめ
今回はaxiosのResponseに型をセットする方法についてご紹介させていただきました。

今回のサンプルデータではuserデータがシンプルなので良いですが、実際に使うデータは似たような名前のプロパティーが多かったりして型推論が使えないと厳しいものが殆どになってくると思います。

しっかりと型をセットすることで、その辺りのTypeScriptの恩恵を受けることができるので積極的に活用していきたいですね。

いつもAnyでやってしまっているという方がいれば、参考になれば幸いです。


# 最後に

明日のカレンダーは @Hideto-Kiyoshima-wc さんになります。

よろしくおねがいします。


ウェブクルーでは一緒に働いてくれる方を絶賛募集中です！
興味のある方はぜひお問い合わせください
https://hrmos.co/pages/1004681658307198976/jobs/214