Help us understand the problem. What is going on with this article?

雰囲気で使わない @types/react

More than 1 year has passed since last update.

@types/react の型定義を雰囲気で使っている皆さん、こんにちは。React コンポーネントを書くときはどの型を使い、どう書くべきか?という疑問を Twitter でお見かけし、雰囲気で使っていた私も気になったので @types/react の中身を覗いてみました。

master に入り、いよいよリリース間近な Hooks。本稿は FC(旧SFC) の話が前提です。VScode で型付けをしようとすると、似たような型が山ほど出てきて、どれを使うのが適切なのか・違いが何なのか…パッと見分かりませんね。

  • ReactNode
  • ReactChild
  • ReactElement
  • StatelessComponent
  • FunctionComponent
  • JSX.Element
  • SFC
  • FC

という訳で、こんな Props を持っているコンポーネントの各種書き方を比較、白黒ハッキリさせていきたいと思います。

type Props = { test: 'test' }

🏆 Winners

結論からいうと、以下二つのどちらかの書き方が良さそうです。

const MyComponentA: React.FunctionComponent<Props> = props => (<></>)
const MyComponentB: React.FC<Props> = props => (<></>)

引数の props に型注釈を入れていませんが、これだけで推論はちゃんと動いてくれます。定義元の React.FunctionComponentprops: P & { children?: ReactNode }が既に入っているので、プログラマー定義のP型 type Props = { test: 'test' } とマージしてくれます。これが勝者たる所以です。React.FC は React.FunctionComponent の定義を参照しているため、全く同じものと言えます。

type FC<P = {}> = FunctionComponent<P>;

React.FunctionComponent が本流ですが、長ったらしいので、React.FC のほうが自分は好みです。つぎに、残念ながら良くなさそうな書き方を紹介します。

🙅‍♀️ children? を自前で入れる必要アリ

基本的に推論に頼りたい派なので、今まで自分はこの書き方をしていました。render children が必要な度、type Propschildren?: ReactNode を自前で注入しなければならず、やや冗長です。

const MyComponentC = (props: Props) => (<></>)
const MyComponentD = (props: Props): JSX.Element => (<></>)
const MyComponentE = (props: Props): React.ReactElement<any> => (<></>)

MyComponentCの戻り型推論はJSX.Elementで、JSX は @types/react の中で切られている namespace です。JSX.Element は、React.ReactElement を空継承しています。つまり、この3つの書き方は全く同じであり、後者二つは戻り型注釈付与の意味がない事になります。

interface Element extends React.ReactElement<any> { }

🙅‍♂️ 型が緩い

特に困ることはないですが、React.ReactElement を含んだ UnionTypes なので次の書き方でもコンパイルは通ります。ReactNode や ReactChild には、FunctionComponent 以外にも色んな型が UnionTypes で入っており、コンポーネント以外の型へも、制限を緩めてしまっています。ReactText の実体は string | number です。

// type ReactChild = ReactElement<any> | ReactText;
const MyComponentF = (props: Props): React.ReactChild => (<></>)

// type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
const MyComponentG = (props: Props): React.ReactNode => (<></>)

こんなコンポーネントを作る人はいないでしょうが、コンパイルエラーを得られていないことから、間違っていることは明らかです。

const MyComponentF = (props: Props): React.ReactChild => (0)
const MyComponentG = (props: Props): React.ReactNode => (null)

😭 そもそも deprecated

@deprecated as of recent React versions, function components can no longer be considered 'stateless'. Please use FunctionComponent instead. @see React Hooks

だそうです。Hooks の影響をもろに受けている…。この書き方をしていたら、気分が向いた時に書き換えましょう。

const MyComponentH: React.StatelessComponent<Props> = props => (<></>)
const MyComponentI: React.SFC<Props> = props => (<></>)

ちなみに、この二つどちらもReact.FCと同様に、React.FunctionComponentのエイリアスでしかないので、定義内容は一緒です。似たような定義山ほどあった理由は、前方互換を担保したりショートハンドを提供するためだった、というお話でした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした