レスポンスに型を付ける
json() が返すものには型が無い
前回のコードには穴があります。
const data = await res.json();
setItems(data);res.json() が返すのは any です。中身が何かは、返ってくるまで誰にも分からないからです。any は「何でもあり」という意味なので、そのまま setItems に渡せてしまいます。仮に API が数字の配列を返しても、文字を返しても、その場では何も言われません。型検査をすり抜ける穴がここに空いています。
as で塞ぐのはごまかし
塞ぎ方として、まずこれを思いつきます。
const data = (await res.json()) as Item[];as は「これは Item の配列だと思ってくれ」という宣言です。TypeScript は確かめずに信じます。中身が違っても黙って通るので、穴の場所を変えただけです。事故は使う場所まで先送りされます。
取得関数の戻り値に型を書く
まともなやり方は、取ってくる仕事を関数に切り出して、その関数が何を返すかを書くことです。
async function fetchItems(): Promise<Item[]> {
const res = await fetch("/api/items");
const data: Item[] = await res.json();
return data;
}async の付いた関数は必ず Promise を返します。だから戻り値の型は Item[] ではなく Promise<Item[]> と書きます。TypeScript入門で出てきた「入れ物の型」です。
こう書くと、呼ぶ側では await fetchItems() の結果がそのまま Item[] になります。
const list = await fetchItems();list は Item[] です。list[0].titel と打ち間違えれば、その場で赤い線が出ます。
境界を1か所にまとめる
大事なのは、外から来たものを型のある世界に入れる場所を1か所にすることです。fetchItems の中だけが API の形を知っていればよく、それ以外の場所は Item[] としか付き合いません。API の返す形が変わっても、直すのはこの関数だけになります。
演習
取得を fetchItems という関数に切り出し、戻り値に型を付けます。