Gatsby.jsでGraphQLのスキーマから型を生成して利用する

TypeScript導入時はコンポーネント内に自分でPropsの型を書いていましたが、全てのコンポーネントでこれを行うと非常に面倒かつ、GraphQLのスキーマとオリジナルの型で二重に型を生成することになるので、自動生成するプラグインを導入してコンポーネントから利用します。

gatsby-plugin-graphql-codegenの導入

npmでインストールした後、生成するtypesファイルの宛先と監視するディレクトリを指定します。 これらはGraphQLのクエリを生成する部分が指定してあればよいので、特に目立った構成変更をしていない自分の場合はREADMEの指定をそのまま利用しました。

{
      resolve: "gatsby-plugin-graphql-codegen",
      options: {
        fileName: "types/graphql-types.ts",
        documentPaths: [
          "./src/**/*.{ts,tsx}",
          "./node_modules/gatsby-*/**/*.js"
        ],
        codegenDelay: 200
      }
    },

出力先fileNameは監視するディレクトdocumentPathsと同じ場所にしてしまうと自分を参照して無限ループになるので注意。

これで、ローカルサーバーを起動した後にクエリに変更を行うとプロジェクト内で使える型データがtypes/graphql-types.tsに吐き出されます。

コンポーネント内で型を利用する

前回までは自分で型を書いていました。

interface StaticQueryProps {
  allMarkdownRemark: {
    edges: Edge[]
  }
}

interface Edge {
  node: {
    frontmatter: {
      date: string
      title: string
    }
    id: string
  }
}

const recentPost: React.FC = () => (
  <StaticQuery
    query={graphql`
      query RecentPostQuery {
        allMarkdownRemark(limit: 4, sort: { fields: frontmatter___date, order: DESC }) {
          edges {
            node {
              frontmatter {
                date(formatString: "YYYY/MM/DD")
                title
              }
              id
            }
          }
        }
      }
    `}
 // render

自動生成した型を使うために、import文を1行書いて型情報を読み込みます。 この場合、GraphQLのクエリに名称をつけておくと[Query名]Queryという名称で型情報が出力されます。

~Queryという名前をクエリにつけていたので、~QueryQueryという型にならないよう全て書き直しました…

//...import
import { RecentPostQuery } from "../../types/graphql-types"

const sportsPost: React.FC = () => {
  const data: RecentPostQuery = useStaticQuery(
    graphql`
      query RecentPost {
        allMarkdownRemark(
          sort: { fields: frontmatter___date, order: DESC }
          limit: 4
        ) {
          edges {
            node {
              frontmatter {
                date(formatString: "YYYY/MM/DD")
                title
                cover {
                  childImageSharp {
                    fluid {
                      src
                    }
                  }
                }
              }
              id
              fields {
                slug
              }
            }
          }
        }
      }
    `
  )
//render

f:id:gensobunya:20200204173243p:plain
vscode capture

VScodeの補完と参照もバッチリ効くので、もうPropsの中身を見る度にあっちこっちのコンポーネントを見る必要なし!

欠点

各Functionの返り値を明確に意識する必要が出てきます。本来なら当たり前ですが、Vanilla JSだとよしなに書いても問題なく動いてしまうので、最初は苦労しました…