Gatsby.js を TypeScript 化する
tsconfig.jsonを追加
tsconfig.json
GraphQL Schema, リクエストの型生成
Gatsby はリソースに対して GraphQL でリクエストを送りデータを取得する GraphQL リクエストのレスポンスの型を、gatsby-plugin-typegen を使い生成する。
yarn add gatsby-plugin-typegen
gatsby-config.js
の plugins にgatsby-plugin-typegen
を追記する。
src/components/index.ts
module.exports = {
siteMetadata: {
// ...
},
plugins: [
// ...
`gatsby-plugin-typegen`
],
}
次に、各コンポーネントの query にクエリ名を追加していきます。
この変更をすることでそのクエリ専用の型が生成されます。
(例: src/components/index.js query BlogIndex
の部分を追記している)
//...
export const pageQuery = graphql`
query BlogIndex {
site {
siteMetadata {
title
}
}
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
nodes {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
}
}
}
}
`
最後にyarn build
を実行すると、src/__generated__/gatsby-types.ts
が生成されているはずです。
ここに GraphQL リクエストの型定義があります。
先ほど追加した BlogIndex クエリの型を見てみると、、
//...
type BlogIndexQueryVariables = Exact<{ [key: string]: never; }>;
type BlogIndexQuery = { readonly site: Maybe<{ readonly siteMetadata: Maybe<Pick<SiteSiteMetadata, 'title'>> }>, readonly allMarkdownRemark: { readonly nodes: ReadonlyArray<(
Pick<MarkdownRemark, 'excerpt'>
& { readonly fields: Maybe<Pick<Fields, 'slug'>>, readonly frontmatter: Maybe<Pick<Frontmatter, 'date' | 'title' | 'description'>> }
)> } };
//...
ちゃんと生成されてますね! 最高便利。
各コンポーネントファイルのTypeScript化
これで準備ができたので、各ファイルを TypeScript 化していきます。
gatsby-plugin-typescriptの追加から入る記事が多いのですが、2020 年 10 月現在、Gatsby にはgatsby-plugin-typescript
がすでに組み込まれているので、何もせずで大丈夫です。
何か TypeScript のビルド関連で追加の設定をしたい場合は、gatsby-config.js の plugins でgatsby-plugin-typescript
を追加して、option を設定してください。
各コンポーネントのファイル拡張子を.js
から.tsx
に書き換えましょう。
そして、StaticQuery の戻り値など型エラーとなっている箇所に型をつけていきます。
例えば、src/pages/index.ts
の型付けは以下のようになります。
import React from "react"
import { Link, graphql } from "gatsby"
import { PageProps } from "gatsby"
import Bio from "../components/bio"
import Layout from "../components/layout"
import SEO from "../components/seo"
const BlogIndex:React.FC<PageProps<GatsbyTypes.BlogIndexQuery>> = ({ data, location }) => {
const siteTitle = data.site?.siteMetadata?.title || `Title`
const posts = data.allMarkdownRemark.nodes
// ... 以下略
}
ポイントは以下のようにReact.FC
、PageProps
などのジェネリクス型を使うことと、gatsby-plugin-typegen
で生成した型を使うことです。
const BlogIndex:React.FC<PageProps<GatsbyTypes.BlogIndexQuery>> = ({ data, location }) => { /* -- */ }
これでdata
の型がBlogIndexQuery
の型で推論されます。
あとは、適宜 Optional Chaining や、Non null Assertion を使って型エラーを解決しましょう。
4. gatsby-Node.jsのTypeScript化
gatsby-node.js
でも TypeScrip で書けるようにしていきます。ここではts-nodeを追加ます。
ここの書き方は@Takepepeさんの以下の記事を参考にさせていただきました。良記事ありがとうございます🙏
Gatsby.js を完全TypeScript化する - Qiita
yarn add -D ts-node
そして、gatsby-config.js
を以下のように変更します。
"use strict"
require("ts-node").register({
compilerOptions: {
module: "commonjs",
target: "esnext",
},
})
require("./src/__generated__/gatsby-types")
const {
createPages,
onCreateNode,
createSchemaCustomization,
} = require("./src/gatsby-node/index")
exports.createPages = createPages
exports.onCreateNode = onCreateNode
exports.createSchemaCustomization = createSchemaCustomization
そして、今までgatsby-node.js
に記述していた内容をsrc/gatsby-node/index.ts
に移動して、型を設定します。
基本的に node の API はGatsbyNode
から型を取得できます。
本当は、allMarkdownRemark
のクエリ部分の方もgatsby-plugin-typegen
で生成したかったのですが、上手く認識してくれませでした。やり方わかる方いたら教えてください🙏
(ドキュメントの Provides utility types for gatsby-node.js.
はまだチェックがついていないので、まだ未対応なのかな?)。
import path from "path"
import { GatsbyNode, Actions } from "gatsby"
import { createFilePath } from "gatsby-source-filesystem"
export const createPages: GatsbyNode["createPages"] = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const blogPost = path.resolve(`./src/templates/blog-post.js`)
const result = await graphql<{ allMarkdownRemark: Pick<GatsbyTypes.Query["allMarkdownRemark"], 'nodes'> }>(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
nodes {
fields {
slug
}
frontmatter {
title
}
}
}
}
`
)
//...
}
}
export const onCreateNode: GatsbyNode["onCreateNode"] = ({ node, actions, getNode }) => {
const { createNodeField } = actions
//...
}
export const createSchemaCustomization: GatsbyNode["createSchemaCustomization"] = async ({ actions }: { actions: Actions}) => {
const { createTypes } = actions
// ...
}
これでgatsby-Node.js
の TypeScript 化も完了です🎉