ReactにTypeScriptを導入したけれどもwarningが消えなくてツラい方々へ

#はじめに
ReactのプロジェクトでTypeScriptを使っていると、しっかりと型定義したつもりなのにwarningがでてしまうことがよくあります。
warningを解消しようとして沼にハマり、最終的にanyで片付けるなんて経験は誰しも1回はあるのではないかと思います。

**...しかし、anyで結局片付けてしまっては、TypeScriptの型安全の利点を発揮できていませんよね?**

ということで今回は、React(functionalコンポーネント)にTypeScriptを導入したとき、私が一度ハマった部分をProps, State, Event, Refsの4つに分けてまとめました。

仕事でTypeScriptを使い始めた方にはもちろん、ReactのポートフォリオにTypeScriptを導入してみたいという未経験エンジニアの方にも役立つ内容になっていると思います。

#Props
Parent(親コンポーネント)からChild(子コンポーネント)にpropsを渡す際、propsの型を定義してあげると以下のチェックをしてくれるという利点があります。

- Parent側: Childに適切なpropsを渡せているかどうか
- Child側: 適切な名前及び型のpropsを使っているかどうか

例えば、propsの型をChildPropsというinterfaceで定義して、`({ color, onClick }: ChildProps)`のようにChildに注釈してあげます。

```tsx:Child.tsx
interface ChildProps {
  color: string;
  onClick: () => void;
}

export const Child = ({ color, onClick }: ChildProps) => {
  return (
    <div>
      {color} <button onClick={onClick}>Click me</button>
    </div>
  );
};
```

Parent側で`color` `onClick`のpropsがしっかりと呼ばれていれば、TypeScriptのwarningがでることはありません。

```tsx:Parent.tsx
const Parent = () => {
  return (
    <Child color="red" onClick={() => console.log('Clicked')} />
};
```

しかし、propsが適切に呼ばれていない場合、以下のように赤波線のwarningがでます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/b75b9d5c-d209-01ec-811d-8aee415be1a5.png" width="600">
赤波線の部分にカーソルをあてると、`color`が呼ばれてませんよ！というwarningの内容を確認することができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/bfc4ec04-f741-3cbd-dae4-e5e54a55e29a.png" width="600">

次の例を考えてみます。
childrenというpropsを新しく加えると、Child側とParent側の両方でwarningがでてしまいます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/02ba7d34-a3a9-5e97-d45d-0eb54c5801e7.png" width="600">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e73d7a5f-3ed1-03b5-7cd2-6d8a9f5763af.png" width="600">

なんでこんなことが起こってしまうのでしょうか？
それは、**Reactでは任意でコンポーネントに以下のプロパティを与えることができるのですが、TypeScriptが型の定義先をReactだと認識しない限り、これらのプロパティを存在しないものと扱ってしまうからです。**

- propTypes
- displayName
- defaultProps
- contextTypes

そのため、TypeScriptがReactのコンポーネントを認識できるように、propsの型を`Child: React.FC<ChildProps>`のように定義してあげる必要があります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/d10cc663-e83f-f746-dc2e-33eb715ccfb6.png" width="700">

このように記述してあげることで、TypeScriptが以下を担保してくれます。

- ChildがReactのfunctionalコンポーネントであること
- ChildがReactコンポーネントのプロパティ(propTypes, displayName, defaultProps, contextTypesなど)をもっていること
- ChildがChildPropsの型のpropsを受け取ること

というわけで、**Propsの型定義をする場合は`React.FC<>`を使いましょう。**

#State
useStateを使う際、型定義をしないとstateを使用する際にwarningがでます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/64b08c19-da3e-2348-1d71-463fe02a3c26.png" width="600">
カーソルをあててwarningの中身を確認してみると、guestsというstateをstring型の配列として使いたいのですが、型定義をしていないためnever型として扱われてしまっていることがわかります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/b61a8509-ce21-c97a-53fa-b175d1f0ccd8.png" width="600">
warningをださないために、stateの型は以下のように定義してあげましょう。

```tsx
const [guests, setGuests] = useState<string[]>([]);
```


次に、名前検索で該当したユーザを表示するようなケースを考えます。

```tsx:UserSearch.tsx
const UserSearch: React.FC = () => {
  const [name, setName] = useState('');
  const [user, setUser] = useState<{ name: string; age: number } | undefined>();

  const onClick = () => {
    const foundUser = users.find((user) => {
      return user.name === name;
    });

    setUser(foundUser);
  };

  return (
    <div>
      User Search
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <button onClick={onClick}>Find User</button>
      <div>{user?.name}</div>
    </div>
  );
};
```

このとき、userというstateがあるのですが、以下のように型定義すると`<div>{user.name}</div>`の部分でwarningがでます。

```tsx
const [user, setUser] = useState<{ name: string; age: number }>();
````
これはコンポーネントが最初にレンダリングされたときやユーザが検索に引っかからなかったときに、userが未定義(undefined)扱いになってしまうためです。

そのため、undefinedとのunion型として以下のように定義してあげる必要があります。

```tsx
const [user, setUser] = useState<{ name: string; age: number } | undefined>();
```
そして、表示側では`user`に`?`を付与してあげます。
こうすることで、`user`にundefinedの可能性があるということをあらかじめReact側に教えることができます。

```tsx
<div>{user?.name}</div>
```

#Events
onChangeなどのイベントハンドラの処理内容を関数化すると、jsx(tsx)の中に書かれていないため型推論が効かず、`e`がany扱いになってしまいます。

```tsx
  const onChange = (e) => {
    console.log(e);
  };
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/d55835e6-8b85-7d9a-27ad-7ed111455ac2.png" width="200">
warning解消のために、React内のイベントハンドラとして以下のように定義してあげましょう。

```tsx
  const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    console.log(e);
  };
```

#Refs
useRefを利用する際、型定義をしていないと読み込んだ先のコンポーネントでwarningがでます。

```tsx
const inputRef = useRef();
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e342bd5e-1191-546f-983b-918cd4a791f1.png" width="600">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/ee54c554-0350-ed99-2f90-d862131cef4a.png" width="600">

そのため、useRefを使うときは、refの要素の型(この場合だと`HTMLInputElement`)を定義してあげます。
また、レンダリングされるまでrefをアタッチする要素(`Input`)は存在しないので、union型でnullも許容してあげる必要があります。

```tsx
const inputRef = useRef<HTMLInputElement | null>(null);
```
以下は各要素の型の一部です。
div要素にrefをアタッチするときは、`HTMLDivElement`を使ってあげればいいということですね。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/26fcdd5a-9d95-b195-7dd7-0dcab6e0afb7.png" width="600">

また、useEffectを使ってレンダリング時にrefをアタッチした要素(`Input`)にフォーカスがあたるようにしたい場合があると思います。
`inputRef.current.focus()`をそのまま使おうとすると、`input.current`にnullやundefinedの可能性が残っているということでwarningがでてしまいます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/a1e1a9ef-1c35-cdf7-4d52-832aea944483.png" width="600">
そんなときは、`inputRef.current`が存在しないときに早期でreturnを返してあげるようにします。
これをType Guardなどと呼んだりします。

```tsx
  useEffect(() => {
    if (!inputRef.current) {
      return;
    }

    inputRef.current.focus();
  }, []);
```

#おわりに
TypeScriptに慣れてくると、PHPのように型宣言がない言語を書くときにすごく不安になります。