)
}
```
のように props の型に children を明示的に追加する必要があるということです。
### VFC が生まれた経緯
FC と VFC の違いが **暗黙的な children を許容しているかどうか** にしかないことが分かりましたが、まさにそこに VFC が生まれた重要な理由がありました。
[VFCがコミットされたPR](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643)の元となった [issue](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/40993) を見てみると、
>I prefer to be explicit with my types, and I noticed something, that all components, at least functional ones that use React.FC or React.FunctionComponent have a generic that unionizes with PropsWithChildren that automatically adds { children?: ReactNode } and I feel that is incorrect or misleading.
つまり、「コンポーネントによっては `children` を使わない場合もあるのに暗黙的に型が追加されているのは誤解を招くのではないか?」という指摘ですが、これに対して型のリポジトリの[メンテナが同意](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/40993#issuecomment-565096389)しています。
なので、**FC をより理想の姿に修正したのが VFC** ということになります。
## VFC を使わない理由
じゃあ VFC を使えば良いんじゃないの?となりますが、あくまで VFC は「PropsWithChildren型を FC の props から消す」という重大な変更を避けるために作られたものです。以下[引用](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/40993#issuecomment-607370492)。
>Instead of a breaking change, have the maintainers considered adding a VoidFunctionComponent type?
>Changing the type of FunctionComponent to remove children could break a ton of things, especially libraries that rely on the specific children: React.ReactNode prop type. By removing it completely, the type of children is up to the component authors and there is a risk of breaking compatibility with those libraries.
また、React v18 に向けた型定義の [RFC](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/46691) には、FC の props から children が削除される予定であると記載されています。
>remove implicitly typed children from React.FC
In TypeScript it's generally easier to add types than remove them.
なので、次のメジャーアップデートによって VFC はお役御免になり削除されるか非推奨になる可能性が高いです。
大した工数にはならないと思いますが、VFC を使うのであれば後々 FC に置き換えるなどの対応が必要になることは念頭に入れておいたほうが良さそうです。また、後述しますが**そもそも FC を使わない理由があるのであえて VFC で書く理由もない**かなと思います。
## FC を使わない理由
### Facebookのリポジトリで FC の型指定を消す PR がマージされている
[facebook/create-react-app](https://github.com/facebook/create-react-app) リポジトリの [#8177](https://github.com/facebook/create-react-app/pull/8177) で、FC の型指定が削除されました。
PRのdescriptionでは FC のデメリットが指摘されています。
デメリットとして紹介されているのが以下の4点で、簡単に説明します。
**1\. Provides an implicit definition of children**
これは上述した、props の型に暗黙的に children が含まれている件です。
**2\. Doesn't support generics.**
この件についてはジェネリックコンポーネントについて知っておかないとピンとこないので、先にジェネリックコンポーネントについて紹介しておきます。以下[引用](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#generic-components)ですが、
```ts
interface Props {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
(中略)
const List = (props: Props) => {
const { items, renderItem } = props;
const [state, setState] = React.useState([]); // You can use type T in List function scope.
return (
}
/>
)
}
```
のように使い回すことができるわけですね。
で、これが FC では作れないよねっていう指摘です。
>But it's not possible when using React.FC - there's no way to preserve the unresolved generic T in the type returned by React.FC.
```ts
const GenericComponent: React.FC* ??? */> = (props: GenericComponentProps) => {/*...*/}
```
**3. Makes "component as namespace pattern" more awkward.**
>"component as namespace pattern"
とは、例えば
```ts
const List = ({ children }: { children: React.ReactNode}) => {
return (
)
}
List.Item = Item
```
ちょっとごちゃごちゃしてしまっているのであえて FC で宣言するメリットは無さそうです。
**4. Doesn't work correctly with defaultProps**
前述したとおり `defaultProps` は現在非推奨となっていますが、PRの指摘によると、「しかもうまく動作しないことがある」とのことらしいですね。そもそも使わないので今回深追いはしません。
以上の4つの理由から、FC を積極的に使う理由はないということですね。
# それでも FC / VFC を使う意義
ここまで FC と VFC を使う必要性のなさを補助する情報ばかりを挙げてきましたが、 FC や VFC など明示的な型定義を行うメリットについても言及しておきたいと思います。
強調しておくと、ここで述べるのは FC や VFC 固有のメリットなどという話ではなく、**明示的な型定義を行うことによるメリット**という話になります。
## Typescriptのコンパイルのパフォーマンス改善に繋がる可能性がある
Typescriptのコンパイルのパフォーマンス観点から見ると、単にコーディングにおいてメリットがないから型指定を行わなくても良いという単純な結論にはなりません。
[Typescriptのパフォーマンスを意識したコーディングのヒント](https://github.com/microsoft/TypeScript/wiki/Performance#using-type-annotations)によると
>Adding type annotations, especially return types, can save the compiler a lot of work. In part, this is because named types tend to be more compact than anonymous types (which the compiler might infer), which reduces the amount of time spent reading and writing declaration files (e.g. for incremental builds).
つまり、**型を明示的に指定するとコンパイルのパフォーマンスが改善される**ということですね。
その観点からであれば FC / VFC または型推論に任せずに `() => JSX.Element` を明示的に型指定してコンポーネントを作っていくという選択肢もありそうです。
>Type inference is very convenient, so there's no need to do this universally - however, it can be a useful thing to try if you've identified a slow section of your code.
一方で、「型推論はとても便利ですが、パフォーマンスが悪ければ型の指定を試してみる価値はあります」とのことなので、使いどころは検討する必要があります。
## 型を指定するとReactコンポーネントであることを明示できる
例えば、コンポーネントの型を特に指定しない場合は以下のような宣言ができてしまいます。
```ts
const Hoge = () => {
return undefined
}
```
Reactコンポーネントは undefined であることは許容されていないため、上記の宣言はReactコンポーネントとしては正しくない状態です。いざ、コンポーネントを使おうとする段階で怒られることになります。
```ts
const App = () => {
return // 'Hoge' cannot be used as a JSX component. Its return type 'undefined' is not a valid JSX element.
}
```
一方、例えば FC を指定しておくと、コンポーネントの宣言時に怒ってくれるようになります。
```ts
const Hoge: React.FC = () => { // Type '() => undefined' is not assignable to type 'FC<{}>'. Type 'undefined' is not assignable to type 'ReactElement | null'.
return undefined
}
```
また、以下のように明示的に返り値の型を JSX.Element型 と指定することで回避することもできます。
```ts
const Hoge = (): JSX.Element => {
return undefined // Type 'undefined' is not assignable to type 'Element'.
}
```
明示的な型指定には以上のように
- Reactコンポーネントとして正しくない宣言をより早い段階で気づくことができるようになる
- 第三者からみてコンポーネントを定義したいという意図が分かりやすい
というメリットがあります。
# まとめ
- 基本的には型推論に任せて `() => JSX.Element` 型のままでOK
- コンパイルのパフォーマンスや保守性を重視するなら明示的に型を指定しよう
# おわりに
お読みいただいてありがとうございました!