投稿日:2021年10月6日
この記事ではTypeScriptでaxiosで利用する際に発生するバグについて、その原因と解決方法について詳しく解説します。
この記事ではTypeScriptでaxiosで利用する際に発生するバグについて、その原因と解決方法について詳しく解説します。
axiosはXMLHttpRequestを簡単に扱うことができるモジュールです。node.jsや各ブラウザで同じ文法で使用することができるため、長年の間広く利用されています。このモジュールはオープンソースで開発が盛んに行われているため、安心感も強いでしょう。また、デフォルトでTypeScriptに対応している点も非常に評価が高いです。
今回のバグはTypeScript+node.jsの環境でaxiosモジュールを使って、とあるREST APIサーバーにHTTPリクエストを送る処理の作成時に発見しました。
そのときのコードは以下のようなものです。
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 Res
でReqの型をResの型に変換しようとしてるけどそんなことはできませんよ。
なんだって!!?
公式のドキュメントによると、axios.post()の返り値のdataプロパティはAPIのレスポンスのbodyをパースされた値のはずです。
では、なぜHTTPレスポンスに謎な型が割り当てられているのでしょうか?
答えはaxiosの型定義にあります。
下がaxios.post()周りの型定義です。
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日)公式が対応中です。
このバグはaxiosのバージョンが0.21から0.22に上がった際に発生しました。
最も簡単な解決方法はaxiosのバージョン0.21台にを下げることです。
$ npm uninstall --save axios
$ npm install --save axios@0.21
ただし、axiosの0.22にしかない機能をすでにいくつも使って実装をすすめている場合などは、バージョンはできるだけ下げたくないですね。
その場合は次の【解決案2】を使うことも考慮しましょう。
先程解説した通り、バグは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も出されていることですし、きっとすぐ修正されるでしょう。