※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【TypeScript】axiosのバージョン0.22.0はresponseのbodyの型がおかしい!?

web開発 フロント開発 axios TypeScript サーバー開発 nodejs node

投稿日:2021年10月6日

このエントリーをはてなブックマークに追加
この記事ではTypeScriptでaxiosで利用する際に発生するバグについて、その原因と解決方法について詳しく解説します。

はじめに

この記事について

この記事ではTypeScriptaxiosで利用する際に発生するバグについて、その原因と解決方法について詳しく解説します。

axiosについて

axiosXMLHttpRequestを簡単に扱うことができるモジュールです。node.jsや各ブラウザで同じ文法で使用することができるため、長年の間広く利用されています。このモジュールはオープンソースで開発が盛んに行われているため、安心感も強いでしょう。また、デフォルトでTypeScriptに対応している点も非常に評価が高いです。

環境

  • node version 10.19.0
  • npm version 6.14.4
  • axios version 0.22.0
  • typescript version 4.4.3

バグの発生

発生した状況

今回のバグはTypeScript+node.jsの環境でaxiosモジュールを使って、とあるREST APIサーバーにHTTPリクエストを送る処理の作成時に発見しました。


そのときのコードは以下のようなものです。

バグの発生したコードのサンプル.ts
import axios from "axios";

/**
 *  リクエストのBodyの型
 */
type Req = {
    topic : string;
}

/**
 * ResponseのBodyの型
 */
type Res = {
    startUrl: string;
    joinUrl: string;
}

// Requestデータの作成
const data:Req = {
    topic: "sample"
}

// サーバーにPost
axios.post("https://example.com",data).then(response => {
    const data = response.data as Res;
    console.log(data);
});

このコード自体に間違いはないのですが、実際にはビルドが失敗してしまいます。

バグの解説

ビルドが失敗した際のエラーの内容は以下のようなものです。

発生したエラー
Conversion of type 'Req' to type 'Res' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'Req' is missing the following properties from type 'Res': startUrl, joinUrlts(2352)

簡単にエラーの内容を説明すると

response.data as ResReqの型をResの型に変換しようとしてるけどそんなことはできませんよ。

なんだって!!?

公式のドキュメントによると、axios.post()の返り値のdataプロパティはAPIのレスポンスのbodyをパースされた値のはずです。

では、なぜHTTPレスポンスに謎な型が割り当てられているのでしょうか?

答えはaxiosの型定義にあります。

下がaxios.post()周りの型定義です。

axiosの型定義
export class Axios {
  
//[...]
  
  post<T = never, R = AxiosResponse<T>>(url: string, data?: T, config?: AxiosRequestConfig<T>): Promise<R>;

//[...]
}

// [...]

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

これを見るとpost()のGeneric引数周りに原因があることがわかります。

post()の返り値の型Promise<R>RはGeneric引数でデフォルトの型が設定されていて、 AxiosResponse<T>post()data引数の型を渡した型になっています。


今回、自分が書いたresponse.data as Resで利用したdataプロパティはAxiosResponse<T>からTの型がそのまま設定されているため、Reqの型になってしまったわけですね。

公式の対応

この問題はすでにissueが発行され、現在(2021年10月5日)公式が対応中です。


解決方法

【解決案1】:axiosのバージョンを下げる

このバグはaxiosのバージョンが0.21から0.22に上がった際に発生しました。
最も簡単な解決方法はaxiosのバージョン0.21台にを下げることです。

ターミナル
$ npm uninstall --save axios
$ npm install --save axios@0.21

ただし、axiosの0.22にしかない機能をすでにいくつも使って実装をすすめている場合などは、バージョンはできるだけ下げたくないですね。

その場合は次の【解決案2】を使うことも考慮しましょう。

解決案2:post<T,R>()のデフォルトの型を使わない

先程解説した通り、バグはpost<T, R>()のGeneric引数のデフォルトの値が原因です。

つまり、型を明確に渡して上げればOKです。

以下の様に書きましょう。

import axios, { AxiosResponse } from "axios";

/**
 *  リクエストのBodyの型
 */
type Req = {
    topic : string;
}

/**
 * ResponseのBodyの型
 */
type Res = {
    startUrl: string;
    joinUrl: string;
}

// Requestデータの作成
const data = {
    topic: "sample"
}

// サーバーにPost
axios.post<Req, AxiosResponse<Res>>("https://example.com",data).then(response => {
    const data = response.data as Res;
    console.log(data);
});

最後に

axiosは開発が非常に盛んにおこなわれています。
issueも出されていることですし、きっとすぐ修正されるでしょう。

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント