Next.js (React) で型安全を意識しよう

## はじめに

この記事はタイトル通り、`Next.js` (`React`) で型安全を意識することを目的としています。

`Typescript` を使っている以上なるべく `any` 等の記述は避けたいところですが、
知っていないと妥協してしまうシチュエーションもあると思います。

なるべく妥協しないように、型安全にまつわる知識もインプットしていきましょう。


### 対象読者

`Next.js` (`React`) × `Typescript` で開発をしているものの、
ついつい `any` を多用してしまっている方。



## 記事の構成

以下の構成になっています。
これらの項目さえ抑えていれば、それなりに、いい感じに、型安全が保障されると思います。

1. 関数コンポーネントの型定義
    → `React.FC<Props>` を使って関数コンポーネントに型定義を付与します。
2. `React.ReactNode` について
    → `children` 等で利用される `React.ReactNode` について説明します。
3. `HTML[○○]Element` を利用する
    → `HTML[○○]Element` により、[○○] に該当するHTML要素として扱えます。
4. HTML要素の属性の型情報
    → HTML要素の属性を、`React.ComponentPropsWithoutRef` で取得します。
5. ユーティリティ型 (`utility type`) を使いこなす
    → ユーティリティ型を使い、既存の型から新しい型を生成します。



## 1. 関数コンポーネントの型定義

`React` の関数コンポーネントの型定義は `React.FC<Props>` を利用します。

ただし、これは記載しなくても `React` 側が型推論でうまいことやってくれるので、
書くか書かないかは割とお好みで決めちゃってもよいかと思います。

一応型定義を記載するメリットを上げるとすれば、

* 関数コンポーネントであるということが一目でわかる
* ジェネリクスを使うことで型情報がまとまってスッキリする

といったところですね。

```react:example
type Props = {
  name: string;
  age: number;
}

const Sample: React.FC<Props> = ({ name, age }) => {
  return <div>{`name: ${name}, age: ${age}`}</div>;
}
```



## 2. React.ReactNode について

`React.ReactNode` は、`React` のコンポーネントが子要素(`children`)として
受け入れることができる型を表します。

具体例：コンポーネント、文字列、`undefined` 、など...

特に `children` の型を指定しないのであれば、`any` ではなく `React.ReactNode` を使いましょう。

```react:example
type Props = {
  children: React.ReactNode;
};

const Sample: React.FC<Props> = ({ children }) => {
  return <div>{children}</div>;
};

const Main: React.FC<Props> = () => {
  return (
    <div>
      <Sample>
        <div>sample</div>
      </Sample>
      <Sample>sample</Sample>
    </div>
  );
};
```



## 3. HTML[○○]Element を利用する

[HTMLDivElement](https://developer.mozilla.org/ja/docs/Web/API/HTMLDivElement) や [HTMLButtonElement](https://developer.mozilla.org/ja/docs/Web/API/HTMLButtonElement) 等いろいろありますが、
これらはHTML要素を表す型として利用することが出来ます。

たとえば、下記の `SampleBtn` ように [forwardRef](https://ja.react.dev/reference/react/forwardRef) を利用する際に
Refで扱う要素の型として、`HTMLButtonElement` を定義することが可能です。

```react:example
import { forwardRef } from 'react';

type Props = {
  children: string;
};

const SampleBtn: React.FC<Props> = forwardRef<HTMLButtonElement, Props>(({ children }, ref) => {
  return <button ref={ref}>{children}</button>;
});
```



## 4. HTML要素の属性の型情報

HTML要素の属性の型情報は、`React.ComponentPropsWithoutRef`、または
`React.ComponentPropsWithRef` で取得可能です。
違いは名前の通り、Refを含むかどうかになります。

```react:example
type Props = { children: React.ReactNode } & React.ComponentPropsWithoutRef<'button'>;

const SampleBtn: React.FC<Props> = ({ children, ...buttonAttributes }) => {
  return <button {...buttonAttributes}>{children}</button>;
};

export const Main = () => {
  return (
    <div>
      {/* button要素の属性をすべて受け付けるので、typeが設定可能 */}
      <SampleBtn type='button'>button</SampleBtn>
    </div>
  );
};

```



## 5. ユーティリティ型 (`utility type`) を使いこなす

[ユーティリティ型](https://typescriptbook.jp/reference/type-reuse/utility-types)を使うことで型の再利用ができるので、
より保守のしやすいソースコードになります。

よく使うのは `Pick` や `Omit` あたりだと思いますが、
一通り目を通しておいていつでも使えるように準備しておきましょう。

以下は記事から引用しているので、気になったらURLを踏んでみてください。


### Required\<T>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/required)。

> Required<T>は、Tのすべてのプロパティからオプショナルであることを意味する?を取り除くユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/1a61fdee-761d-0d25-8e77-22d02de05105.png)


### Readonly\<T>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/readonly)。

> Readonly<T>は、オブジェクトの型Tのプロパティをすべて読み取り専用にするユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/55345a8b-c7e3-012f-f4a2-95b815acf6f7.png)


### Partial\<T>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/partial)。

> Partial<T>は、オブジェクトの型Tのすべてのプロパティをオプションプロパティにするユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/7c046ada-4121-6c40-a841-3f1572a474de.png)


### Pick\<T, Keys>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/pick)。

> Pick<T, Keys>は、型TからKeysに指定したキーだけを含むオブジェクトの型を返すユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/504670ba-54b0-7569-0b53-a647c1ecbd5f.png)


### Omit\<T, Keys>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/omit)。

> Omit<T, Keys>は、オブジェクトの型TからKeysで指定したプロパティを除いたobject型を返すユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/c65b5197-970a-cc9b-8257-b7eae015abb4.png)



### Exclude\<T, U>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/exclude)。

> Exclude<T, U>は、ユニオン型TからUで指定した型を取り除いたユニオン型を返すユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/de42bb7a-897a-a46b-2409-f91a1fecb32d.png)



### Extract\<T, U>
公式は[こちら](https://typescriptbook.jp/reference/type-reuse/utility-types/extract)。

> Extract<T, U>は、ユニオン型TからUで指定した型だけを抽出した型を返すユーティリティ型です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/1ef7c48b-bf57-c893-b8c5-5cce215ab92d.png)


### ※上記のユーティリティ型をカスタマイズしよう

`Required\<T>` や `Readonly\<T>` はそのまま利用することはほとんどなく、
大抵はあるプロパティだけ必須にしたいといったケースになるでしょう。

そういった場合は状況に応じてユーティリティ型をカスタマイズすることになります。

もとある型をうまく利用して、一貫性のあるソースコードを目指したいですね！

```typescript:あるプロパティだけ required にする AddRequired を作成
type AddRequired<T, U extends keyof T> = Omit<T, U> & Required<Pick<T, U>>;

type BaseProps = {
  children: React.ReactNode;
  disabled: boolean;
};

type Props = AddRequired<BaseProps, 'children'>;
```



## おわりに
`typescript` を使っている以上、なるべく `any` はさけて型安全な実装を心掛けたい
といったお話でした。

これは品質にも関わるところなので、妥協せずにインプットを続けていこうと思います！