🗂

【TypeScript】Axiosのリクエストとレスポンスに型をつける

2021/11/13に公開

概要

React×TypeScript のプロジェクトで Axios を使った場合のリクエストとレスポンスの型についてまとめます。最終的にやることはフェイク API の JSONPlaceholder からユーザー情報を 10 件取得してそれらの情報を表示することです。最終的なコードの全容は「コードの全容」に書いてあるので急ぎの方はそちらを見てください!

この記事の中身について説明します。よくある記事としてはプレーンな JavaScript で書いた

axios.get(url);

に TypeScript で型をつける方法です。しかし私は

const options = {
  url,
  method: "GET",
};
axios(options);

と書いたものに型をつけたいと思ってました。

どこか間違いがあったりコードや文章の書き方のアドバイスがありましたらお気軽にコメントしていただければうれしいです!

事前準備

まずは Create React App でプロジェクトを作成します。

npx create-react-app . --template typescript

メインである Axios をインストールします。

npm i axios

リクエストaxios(options)optionsの型

optionsには以下のようにAxiosRequestConfigという型を当てます。AxiosRequestConfigaxiosから import します。

import axios, { AxiosRequestConfig } from "axios";

const url = "https://jsonplaceholder.typicode.com";

const options: AxiosRequestConfig = {
  url: `${url}/users`,
  method: "GET",
};

レスポンスの型

レスポンスには以下のようにAxiosResponseという型を当てます。AxiosResponseaxiosから import します。本題とはちょっとそれるかもしれませんがエラーの型はAxiosErrorという型を当て、こちらもaxiosから import します。

import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
import user from "./user.json"; //レスポンスのJSON(詳しくは補足で)

type USER = typeof user; //画面に表示するユーザー情報の型

const options: AxiosRequestConfig = {
  url: `${url}/users`,
  method: "GET",
};

axios(options)
  .then((res: AxiosResponse<USER[]>) => {
    const { data, status } = res;
    //やりたいことをやる
  })
  .catch((e: AxiosError<{ error: string }>) => {
    // エラー処理
    console.log(e.message);
  });

コードの全容

API 通信はuseEffectの中で行いました。ユーザー情報の表示の部分もまだ記載していなかったのでここでコードの全容を書きます。

ユーザー情報の配列とレスポンスのステータスは以下のように定義しています。

const [users, setUsers] = useState<USER[]>([]);
const [status, setStatus] = useState<number | null>(null);
AxiosResReqType.tsx
import React, { useState, useEffect } from "react";
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
import user from "./user.json"; // レスポンスのJSON(詳しくは補足で)
const url = "https://jsonplaceholder.typicode.com";

type USER = typeof user; // 画面に表示するユーザー情報の型

const options: AxiosRequestConfig = {
  url: `${url}/users`,
  method: "GET",
};

const AxiosResReqType: React.FC = () => {
  const [users, setUsers] = useState<USER[]>([]);
  const [status, setStatus] = useState<number | null>(null);

  //API通信を行う箇所
  useEffect(() => {
    axios(options)
      .then((res: AxiosResponse<USER[]>) => {
        const { data, status } = res;
        setUsers(data);
        setStatus(status);
      })
      .catch((e: AxiosError<{ error: string }>) => {
        // エラー処理
        console.log(e.message);
      });
  }, []);

  //ユーザー情報を表示する箇所
  return (
    <div>
      <h1>Axiosのリクエストとレスポンスに型をつける</h1>
      <h2>ステータス:{status}</h2>
      <ul>
        {users.map(({ id, name }) => {
          return (
            <li key={id}>
              {id} : {name}
            </li>
          );
        })}
      </ul>
    </div>
  );
};

export default AxiosResReqType;

まとめ

  • リクエスト(axios(options)options)に使う型:AxiosRequestConfig
  • レスポンスに使う型:AxiosResponse<T>
    • T には自分の必要な型を書く
    • この記事の場合はUser

まとめるとたった 2 つだけでした笑。

リクエストとレスポンスの型には直接関係ないですが以下のように JSON を使って簡単に型を定義できるのは驚きでした。

import user from "./user.json";
type USER typeof user

冒頭にも書きましたがスペルミス、間違っている箇所、もっとこうしたほうがいいなどのアドバイスがありましたらお気軽にコメントしていただけるとうれしいです!

補足

本筋とは外れますが残しておきたかったことをここに書いておきます。読まなくてもこの記事で言いたいことはわかります!

補足その 1AxiosRequestConfigの型をつける必要わかった経緯

ここは型をつける理由がわかった経緯を書いているので読まなくても型をつける方法はわかります。

最初は何も考えずに以下のように書いてました。

const options: AxiosRequestConfig = {
  url: `${url}/users`,
  method: "GET",
};
axios(options);

この時 VS Code 上でoptionsのところに以下のアラートが出てきました。

No overload matches this call.
  Overload 1 of 2, '(config: AxiosRequestConfig<any>): AxiosPromise<any>', gave the following error.
    Argument of type '{ url: string; method: string; }' is not assignable to parameter of type 'AxiosRequestConfig<any>'.
  Overload 2 of 2, '(url: string, config?: AxiosRequestConfig<any> | undefined): AxiosPromise<any>', gave the following error.
    Argument of type '{ url: string; method: string; }' is not assignable to parameter of type 'string'.ts(2769)

No overload matches this call.の意味はわかりませんでしたが(誰か教えてください)、ざっくり意訳してみると「型{ url: string; method: string; }が型AxiosRequestConfig<any>に割り当てられない」という感じです。options型をあてていないので型推論により{ url: string; method: string; }が割り当てられたようです。なのでoptionsに型anyをつけても問題は解消されます(されてない)。

補足その 2AxiosResponseの型をつける必要わかった経緯

optionsに型AxiosRequestConfigを適用することがわかった後の話です。VS Code 上でAxiosRequestConfigを ctrl +クリックするとnode_modules/axios/index.d.tsのファイルが表示されます。ここに型の情報がいろいろ書かれています。

この中にレスポンスに関する型もあると予想しresponseで検索をかけると以下の記述がありました。

export interface AxiosResponse<T = any, D = any> {
  data: T;
  status: number;
  statusText: string;
  headers: AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;
}

これを見つけた時レスポンスに使う型はこれかなと思いました。その理由は以下の 2 つです。

  • レスポンスに含まれるdataという変数に欲しい情報(今回の場合はユーザー情報)が入ることを把握していた
  • dataはどのようなリクエストをするかによって変数の型は変わるのでジェネリクスでUSER型をどこかで指定しないといけないのはなんとなく予想していた

よって以下ようにres:AxiosResponse<USER[]>を書きました。今回はユーザー情報を 10 件取得するので配列にしています。datastatusにもきっちり型が効いてます。

axios(options).then((res: AxiosResponse<USER[]>) => {
  const { data, status } = res;
  //やりたいことをやる
});

補足その 3(user.json とtype USER = typeof user;に関して)

1 つのユーザー情報には id,name,email などたくさんあるのでそれを 1 つずつ{"id":number,name:string,"email":string,...}と 1 つずつ書いていくとかなり手間になります。楽してUSERの型を定義するタメに JSON とtypeofを使います。

user.json はこちらの JSONPlaceholderにアクセスして表示される配列の 1 つ目の要素をコピペしたものです。

user.json(ちょっと長め)
user.json
{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "Sincere@april.biz",
  "address": {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
      "lat": "-37.3159",
      "lng": "81.1496"
    }
  },
  "phone": "1-770-736-8031 x56442",
  "website": "hildegard.org",
  "company": {
    "name": "Romaguera-Crona",
    "catchPhrase": "Multi-layered client-server neural-net",
    "bs": "harness real-time e-markets"
  }
}

この JSON に対して

type USER typeof user

とすると簡単に USER 型が得られます。ちなみに VS Code 上でUSERをホバーすると以下の画像のようなものが表示されます。

GitHubで編集を提案

Discussion