ReactコンポーネントのTypeScript型指定を比較する

公開日:
目次

Reactコンポーネントを書くとき、型の指定方法がいくつかあります。React.FCReact.JSX.ElementReact.ReactElement のどれを使うべきか気になりました。複数のソースを調べてみたので備忘録です。

// パターン1: React.FC
const App: React.FC<AppProps> = ({ message }) => <div>{message}</div>;

// パターン2: 戻り値型を指定
const App = ({ message }: AppProps): React.JSX.Element => <div>{message}</div>;

// パターン3: 型推論に任せる
const App = ({ message }: AppProps) => <div>{message}</div>;

結論

型推論に任せる(パターン3) のが現在のベストプラクティスです。

複数の公式・準公式ソースを調べた結果、以下の理由から型の省略が推奨されていました。

ソース 推奨内容
React公式ドキュメント[1] サンプルコードで戻り値型を省略
React TypeScript Cheatsheet[2] 「return type is inferred」が最も簡単な方法
Total TypeScript[3] 戻り値型を省略し、型推論に任せる
Create React App[4] テンプレートから React.FC を削除
// 推奨:型推論に任せる
export function MyComponent({ message }: AppProps) {
  return <div>{message}</div>;
}

なぜ省略が推奨されるのか

React TypeScript Cheatsheetでは、戻り値型を省略するパターンを「Easiest way to declare a Function Component」と説明しています[2:1]。理由は以下の通りです。

  • TypeScriptの型推論が十分に賢く、正しい型を自動で推論できる
  • 冗長な型注釈を避けられる
  • コードの変更に対して型が自動で追従する

React公式ドキュメントでも、すべてのサンプルコードでコンポーネントの戻り値型を省略しています[1:1]。Hooksの説明でも「you will get inferred types a lot of the time and ideally do not need to handle the minutiae of providing the types」と型推論の活用を推奨しています。

明示する場合の選択肢

リンターで戻り値型を強制している場合など、明示する必要があるケースもあります。その場合、2つのアプローチがあります。

React.FC を使う

const App: React.FC<AppProps> = ({ message }) => <div>{message}</div>;

コンポーネント全体の型を指定する方法です。React TypeScript Cheatsheetによると、最新のReact型とTypeScript 5.1以降では「mostly a stylistic choice」(スタイルの好みの問題)とされています[2:2]。ただし、それ以前の環境では非推奨(discouraged)です。

以前の React.FC は暗黙的に children を含んでいましたが、React 18以降でこの問題は修正されました。

戻り値型を指定する

const App = ({ message }: AppProps): React.JSX.Element => <div>{message}</div>;

戻り値の型のみを指定する方法です。React TypeScript Cheatsheetでは、明示する場合この書き方を例示しています[2:3]

戻り値型の選択肢は以下の通りです。

説明
JSX.Element グローバル名前空間の型(従来の書き方)
React.ReactElement Reactの型定義
React.JSX.Element React名前空間の型(React 18.2以降の書き方)

JSX.ElementReact.ReactElement は機能的に同じ型です[3:1]。React 18.2.0以降、@types/react はグローバル名前空間の汚染を避けるため React.JSX 名前空間を導入しました。新しいコードでは React.JSX.Element を使う方がモダンです。

React.FC vs React.JSX.Element

明示する場合、どちらを選ぶべきでしょうか。

観点 React.FC React.JSX.Element
指定する範囲 コンポーネント全体 戻り値のみ
propsの書き方 ジェネリクスで指定 引数で指定
関数宣言との相性 アロー関数向き どちらでも可
ジェネリクス対応 非対応 対応
// React.FC: propsをジェネリクスで指定
const App: React.FC<AppProps> = ({ message }) => <div>{message}</div>;

// React.JSX.Element: propsを引数で指定
function App({ message }: AppProps): React.JSX.Element {
  return <div>{message}</div>;
}

React.FC はジェネリックコンポーネントを作成できないという欠点があります[5]

// ジェネリックコンポーネント:React.JSX.Element なら可能
function List<T>({ items }: { items: T[] }): React.JSX.Element {
  return <ul>{items.map((item, i) => <li key={i}>{String(item)}</li>)}</ul>;
}

// React.FC ではジェネリクスを使えない
const List: React.FC<{ items: T[] }> = ({ items }) => { ... }; // エラー

また、vercel/commerceやbulletproof-reactなどの有名リポジトリでも React.FC は使われていないそうです[5:1]function 宣言を使いたい場合は戻り値型を指定する方が自然です。

現状のベストプラクティス

基本は省略、明示したい場合は React.FC または React.JSX.Element を使うのが現在のベストプラクティスのようです。

私はリンター + function 宣言を使っているので、これからは React.JSX.Element を使っていこうと思います。

脚注
  1. Using TypeScript - React公式ドキュメント ↩︎ ↩︎

  2. Function Components - React TypeScript Cheatsheet ↩︎ ↩︎ ↩︎ ↩︎

  3. React.ReactNode vs JSX.Element vs React.ReactElement - Total TypeScript ↩︎ ↩︎

  4. Remove React.FC from Typescript template - Create React App PR #8177 ↩︎

  5. 【Reactの型定義】FCとJSX.Elementどっち使えば良い? - Zenn ↩︎ ↩︎