hooksの型推論が効かなかったのはTypeScriptのas constがわかっていなかったから
久しぶりにReactを触っているのですが、hooksを自作したら分割代入したときにTypeScriptの型推論が効かなかったマンです。
hooksの型推論が思うように効かない
下のようにAPIを呼び出して、結果を使うhooksを作りました。
import React, { useState, useEffect } from 'react'; import yelp from '../api/yelp'; import { SearchResult } from '../types'; export default () => { const [results, setResults] = useState<SearchResult[]>([]); const [errorMessage, setErrorMessage] = useState<string>(''); const searchApi = async (term: string) => { // 省略:検索の非同期処理 }; useEffect(() => { searchApi('pasta'); }, []); return [searchApi, results, errorMessage] as const; };
使う側でいい感じに型を推論してほしかったのですが、分割代入すると思うような結果にならなかったです。。
hooksの作り方が悪いのかなと思ったら普通にTypeScriptのことをわかっていないだけでした。
TypeScriptにおける配列の型推論
下のようなコードをTypeScriptで書いてみました。配列を作って、その配列から分割代入してます。
const ary = [1, 'hoge', true, () => 'moge', {a: 'a', b: 'b'}]; const [one, hoge, trueValue, mogeFunc, obj] = ary;
普通に型推論が効くかと思いきや、残念な結果に。。。
oneは 1
になってほしいところですが、以下のように「いずれかの型」という推論しかされません。
const one: string | number | boolean | (() => string) | { a: string; b: string; }
当然、この状態では加算だってできません。。。
以下にplaygroundを用意したので気になる人は試してみてください。
TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript
const assertions
この現象はTypeScript3.4に追加された「const assertions」についての理解が乏しかったせいでした。配列やオブジェクトなどに as const
をつけることでコンパイラに「中身は変わりませんよ」というのを教えてあげる必要があるのです。
ということで ary
に as const
をつけてあげることで、型推論がちゃんと効くようになります!
const ary = [1, 'hoge', true, () => 'moge', {a: 'a', b: 'b'}] as const; // ここに追加 const [one, hoge, trueValue, mogeFunc, obj] = ary;
hooksに関しても原因は一緒
冒頭のhooksに関しても、 return
するものに as const
をつけてあげることでちゃんと型推論が効きます。
return [searchApi, results, errorMessage] as const; // ここに追加
これでhooksの使う側も型推論が効いて心穏やかにコーディングできます。