React + TypeScript で defaultProps の型定義をする


GatsbyJS をTypeScript化してみて気付いたのですが、
defaultPropsを設定した際に props の型はundefined型も許可してしまう問題があります。これではせっかくpropsの型定義をしても意味が無いですね。
そこで GatsbyJS の src/components/header.js をTypeScript化して header.tsx にしてみたものを例として解決方法を提示しておきます。
※GatsbyJS の src/components/header.js には defaultProps が元から定義してあるのですが、defaultPropsは最終的に廃止される予定とのことなので設定するかどうかについては注意です。詳細はこちらをご参照ください。

悪いdefaultPropsの設定例

(悪い例)src/components/header.tsx
.
.
type Props = {
  siteTitle: string
}

const Header: React.FC<Props> = ({ siteTitle }) => (
  <header
    style={{
      background: `rebeccapurple`,
      marginBottom: `1.45rem`,
    }}
  >
  .
  .
  </header>
)

Header.defaultProps = {
  siteTitle: ``, // ここがstring以外の型だったら、一応は下記画像のように怒ってくれます。しかし、undefined型だと怒ってくれません。
}

defaultProps の型が string と undefined 以外だと、 Props の型と違うので怒ってくれる例

undefined だと怒ってくれません

解決方法

方法としては、
Propsの型定義をする際に、Intersection Types(&)typeofを使用して、 defaultProps の型定義も一緒にしてしまう方法です。
まずは defaultProps は const で変数として宣言してあげて、それをPropsの型定義をする際に一緒に当て嵌めてしまいます。
そして定義した defaultProps を Header.defaultProps に代入してあげます。
詳細は以下になります。

(良い例)src/components/header.tsx
.
.
type Props = {
  siteTitle: string
} & typeof defaultProps

const defaultProps = {
  siteTitle: ``,
}

const Header: React.FC<Props> = ({ siteTitle }) => (
  <header
    style={{
      background: `rebeccapurple`,
      marginBottom: `1.45rem`,
    }}
  >
  .
  .
  </header>
)

Header.defaultProps = defaultProps

defaultProps の値をstring型以外の型の値に設定してみた場合はもちろん怒ってくれますし、

defaultProps の値を undefined に設定してみた場合も、怒ってくれるようになりました。

参考にさせていただいたURL

Typing defaultProps