投稿日:2021年2月6日
TypeScriptでReactアプリケーションを作成した場合、関数型コンポーネントの引数の型指定に少し注意が必要です。
TypeScriptは型付き言語であるため関数の引数の型は常に意識する必要があります。
Reactのコンポーネントを記述するときも同様です。
propsを受け取らない場合はjavascriptで記述する場合と変わりません。
import React from "react";
const SampleComponent = () => {
return (<div>Hello World!!</div>)
}
export default SampleComponent;
このとき引数の型は指定していませんが実際には暗黙的な型推論が行われ、
この関数はReact.FC<{}>という型エイリアスとして認識されています。
つまり型指定して書くと以下のように書くことができます。
import React from "react";
const SampleComponent: React.FC<{}> = () => {
return (<div>Hello World</div>)
}
ジェネリック型エイリアスのReact.FC<P>のPはpropsの型です。
しかし、はじめの例ではPを指定しませんでした。
なぜでしょうか?
ここでReact.FC<P>の定義を見ると、以下のようになっています。
type FC<P = {}> = FunctionComponent<P>;
なるほど、Generic型エイリアスのPにはデフォルトの型として空の型{}が指定してあったんですね。
propsとして記述する場合には以下のように書きます。
import React from "react";
interface Props {
name: string;
age: number;
children?: React.ReactNode;
}
const SampleComponent = (props: Props) => {
return (<div>
<div>{props.name}</div>
<div>{props.age}</div>
{props.children && <div>{props.children}</div>}
</div>)
}
export default SampleComponent;
Propsという型エイリアスではchildrenをオプション引数として受け取っています。
childrenにはタグの中の子要素が自動的に渡されます。
タグの中に入る子要素はJSXで記述された要素、JSXで記述された複数の要素、文字列、複数の文字列と4つのパターンが考えられます。
Reactは子要素として考えられる上記すべての要素をカバーしたReact.ReactNodeという型を提供しています。
propsの型指定はReact.FC<P>をつかって以下のように書くこともできます。
import React from "react";
interface Props {
name: string;
age: number;
}
const SampleComponent: React.FC<Props> = (props) => {
return (<div>
<div>{props.name}</div>
<div>{props.age}</div>
{props.children && <div>{props.children}</div>}
</div>)
}
export default SampleComponent;
ここでインターフェイスPropsのオプション引数であるchildrenが削除されたことに注意してください。
実はReact.FC<P>のPという型は内部でchildrenという値をオプションで受け取れるように拡張されているのです。
前の例のようにpropsに型を指定した際には最終的な型がPropsになるのですが、React.FC<P>で指定した場合はchildrenが拡張された型が最終的なpropsの型になるのです。
propsの型の指定とReact.FC<P>の型エイリアスを使った型指定、どちらでも良さそうですが、どちらを使うべきなのでしょうか?
結論からいうと、僕は必ずpropsの型指定にすべきだと考えています。
僕はTypeScriptで書く際には受け取れる値についてはすべて明確にしておくべきだと考えています。
React.FC<P>で書いた場合、childrenをレンダリングする機能がコンポーネントに備わっていなくてもchildrenが受け取れてしまいます。
そのため複数人のプロジェクトの場合、
「あの人が作ったこのコンポーネント、子要素がレンダリングされないぞ?バグかな〜…」
といった事態になりかねません。
せっかくTypeScriptを使ったのだからより安全に書きたいですね〜。