GatsbyJSチュートリアルの内容をTypeScript化させてみた

チュートリアル4~8までの内容をTypeScript化してみた。<a href="https://www.gatsbyjs.com/tutorial/part-four/">Data in Gatsbyより</a>

GraphQL周りの型はgatsby-plugin-typegenというプラグインで型生成できるため楽に実装できた。
(<a href="https://zenn.dev/ryo_kawamata/articles/gatsby-ts-2020">Gatsby.jsのTypeScript化 2020を大いに参考。</a>Zennのサイトへ飛びます。)
※ただし１箇所だけ最後まで分からなかった問題はあり（必要な箇所で後述します）

全体として動かすために最低限必要なTS化となっており細かいところはどうか許してやってください（指摘はもちろんもらえたら嬉しいです）

## 成果物
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651103/2ed64012-a8a0-97be-5417-980bedb79f58.png)

TS化→http://localhost:9000/ でも同様に立ち上げることができたという画像


<a href="https://github.com/yoshisansan/gatsby-typescript-tutorial/tree/js-version">チュートリアルをデフォルト(JavaScript)で終えたブランチはココ</a>
<a href="https://github.com/yoshisansan/gatsby-typescript-tutorial/tree/main">チュートリアルをTS化したブランチ(完成形)はココ</a>

## 手順

1. typescriptの環境構築
2. GraphQLの型生成
3. 全ページのTS化


## 1. typescriptの環境構築
"tsc --init"で初期化されたtsconfig.jsonファイルを作成します。

```
npx tsc --init
```

次にtypescriptに必要なライブラリを２つ落としてきます。

```
yarn add -D typescript
yarn add gatsby-plugin-typegen // gatsby-config.jsのplugins[]へ追記
```

### tsconfig.jsonの設定
最初はstrictモードのオプションを外しておきます。

```tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": ["dom", "es2017"],                             /* Specify library files to be included in the compilation. */
    "jsx": "react",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    "outDir": "./build",                        /* Redirect output structure to the directory. */
    "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    "noUnusedLocals": true,                /* Report errors on unused locals. */
    "noUnusedParameters": true,            /* Report errors on unused parameters. */
    "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
    "baseUrl": "src",                       /* Base directory to resolve non-absolute module names. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
    // "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
  },
  "include": ["src/**/*", "gatsby-node/index.ts"],
  "exclude": ["node_modules", "public", "build", "src/templates/blog-post.tsx"],
}

```

### gatsby-config.jsへ追記

```gatsby-config.js
module.exports = {
  siteMetadata: {
    title: `typescriptのテスト gatsbyのチュートリアル参考：https://www.gatsbyjs.com/tutorial/part-four/`,
    description: `これは説明文章ですよ`,
    author: `gatsbyJSマン`,
  },
  plugins: [
    `gatsby-plugin-emotion`,
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `src`,
        path: `${__dirname}/src/`,
      }
    },
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `GatsbyJS`,
        short_name: `GatsbyJS`,
        start_url: `/`,
        background_color: `#6b37bf`,
        theme_color: `#6b37bf`,
        display: `standalone`,
        icon: `src/images/icon.png`,
      },
    },
    `gatsby-plugin-offline`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-typegen`, // 型生成のプラグインを追加
  ],
}
```

## 2. GraphQLの型生成
どこからでも良いのですが、まずindex.jsのファイルでGraphQLの型を生成してみます。

index.tsxへファイル名を変更。当然ですがそのままだと型エラーが表示されます(エディターはVSCodeを使用)。
下記のように書き換えます。

```index.tsx
/** @jsx jsx */ //emotionCSSのTS化のための記述
import React, {FC} from "react"
import { jsx, css } from '@emotion/react' // jsxを追加
import { Link, graphql } from "gatsby"
import { rhythm } from "../utils/typography"
import Layout from "components/layout"

const Home: FC<{ data: any }> = ({data}): any => { //型生成するまで適当にanyを突っ込んでおく
  return (
    <Layout>
      <div>
        <h1
          css={css`
            display: inline-block;
            border-bottom: 1px solid;
          `}
        >
          Amazing Pandas Eating Things
        </h1>
        <h4>{data.allMarkdownRemark.totalCount} Posts</h4>
        {data.allMarkdownRemark.edges.map(({ node }) => (
          <div key={node.id}>
            <Link
              to={node.fields.slug}
              css={css`
                text-decoration: none;
                color: inherit;
              `}
            >
            <h3
              css={css`
                margin-bottom: ${rhythm(1 / 4)};
              `}
            >
              {node.frontmatter.title}{" "}
              <span
                css={css`
                  color: #bbb;
                `}
              >
                — {node.frontmatter.date}
              </span>
            </h3>
            <p>{node.excerpt}</p>
            </Link>
          </div>
        ))}
      </div>
    </Layout>
  )
}

export const query = graphql`
  query MarkdownOfIndex { //便宜上、任意で型の名前をつけておく（ただし、名前がなくても生成されるファイルの type Query オブジェクトの中に型が格納されているので取り出せば良い）
    allMarkdownRemark {
      totalCount
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "DD MMMM, YYYY")
          }
          fields {
            slug
          }
          excerpt
        }
      }
    }
  }
`

export default Home
```

この状態で、

```
gatsby build
```

してあげると、srcディレクトリ直下に __ generated __ ファイルが生成されて中に型情報ファイルが入っています。
ファイル検索で"MarkdownOfIndex"をかけてみると生成された型を確認できるのでこれを引っ張ってあげましょう。

```gatsby-typescript.ts

type MarkdownOfIndexQueryVariables = Exact<{ [key: string]: never; }>;


type MarkdownOfIndexQuery = { readonly allMarkdownRemark: ( //この型を引っ張ってあげる
    Pick<MarkdownRemarkConnection, 'totalCount'>
    & { readonly edges: ReadonlyArray<{ readonly node: (
        Pick<MarkdownRemark, 'id' | 'excerpt'>
        & { readonly frontmatter: Maybe<Pick<MarkdownRemarkFrontmatter, 'title' | 'date'>>, readonly fields: Maybe<Pick<MarkdownRemarkFields, 'slug'>> }
      ) }> }
  ) };


```

### GatsbyのPagePropsライブラリを使う

index.tsxに戻りPagePropsを追加

```index.tsx
import { Link, graphql, PageProps } from "gatsby"
```

そして下記のように型を嵌めれば上手くいく。

```index.tsx
const Home: FC<PageProps<GatsbyTypes.MarkdownOfIndexQuery>> = ({data}) => {
  return (
    <Layout>
//以下略
```

## 3. srcの全ページのTS化
2.と同じ要領で生成した型を当てていきます。

### build前の状態
それぞれのファイルを最低限の書き換えでbuildできる状態にします。この段階でもし怒られた箇所があれば適当にanyなどをはめて一時凌ぎを。

```components/layout.tsx
/** @jsx jsx */
import React, {FC} from "react"
import { jsx, css } from "@emotion/react"
import { useStaticQuery, Link, graphql } from "gatsby"
import { rhythm } from "../utils/typography"

const Layout = ({ children }) => {
  const data = useStaticQuery<GatsbyTypes.LayoutSiteMetadataQuery>(
    graphql`
    query LayoutSiteMetadata {
      site {
        siteMetadata {
          title
        }
      }
    }
    `
  )
  return (
    <div
      css={css`
        margin: 0 auto;
        max-width: 700px;
        padding: ${rhythm(2)};
        padding-top: ${rhythm(1.5)};
      `}
    >
      <Link to={`/`}>
        <h3
          css={css`
            margin-bottom: ${rhythm(2)};
            display: inline-block;
            font-style: normal;
          `}
        >
          {data.site.siteMetadata.title}
        </h3>
      </Link>
      <Link
        to={`/about/`}
        css={css`
          float: right;
        `}
      >
        About
      </Link>
      {children}
    </div>
  )
}

export default Layout
```


```components/seo.tsx
import React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ description, lang, meta, title }) => {
  const { site } = useStaticQuery<GatsbyTypes.SEOsiteMetadataQuery>(
    graphql`
      query SEOsiteMetadata {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )
  const metaDescription = description || site.siteMetadata.description
  return (
    <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={`%s | ${site.siteMetadata.title}`}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: site.siteMetadata.author,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
      ].concat(meta)}
    />
  )
}
SEO.defaultProps = {
  lang: `en`,
  meta: [],
  description: ``,
}
SEO.propTypes = {
  description: PropTypes.string,
  lang: PropTypes.string,
  meta: PropTypes.arrayOf(PropTypes.object),
  title: PropTypes.string.isRequired,
}
export default SEO

```


```pages/about.tsx
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

const About = ({ data }) => {
  return (
    <Layout>
      <h1>About {data.site.siteMetadata.title}</h1>
      <p>
        We are the only site running on your computer dedicated to showing the
        best photos and videos of pandas eating lots of food.
      </p>
    </Layout>
  )
}

export const query = graphql`
  query AboutsiteMetadata {
    site {
      siteMetadata {
        title
      }
    }
  }
`

export default About

```

```pages/my-files.tsx


import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

const MyFiles = ({data}) => {
  console.log(data)
  return (
    <Layout>
      <div>
        <h1>My Sites Files</h1>
        <table>
          <thead>
            <tr>
              <th>relativePath</th>
              <th>prettySize</th>
              <th>extension</th>
              <th>birthTime</th>
            </tr>
          </thead>
          <tbody>
            {data.allFile.edges.map(({ node }, index) => (
              <tr key={index}>
                <td>{node.relativePath}</td>
                <td>{node.prettySize}</td>
                <td>{node.extension}</td>
                <td>{node.birthTime}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query {
    allFile {
      edges {
        node {
          relativePath
          prettySize
          extension
          birthTime(fromNow: true)
        }
      }
    }
  }
`

export default MyFiles

```

```templates/blog-post.tsx
import React from "react"
import { graphql } from 'gatsby'
import Layout from "../components/layout"
import SEO from "../components/seo"

const BlogPost = ({data}) => {
  const post = data.markdownRemark
  return (
    <Layout>
      <SEO title={post.frontmatter.title} description={post.excerpt} />
      <div>
        <h1>{post.frontmatter.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post.html }}/>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
      excerpt
    }
  }
`

export default BlogPost

```


typography.jsはtypography.tsへファイル名のみ変更


```
gatsby build
```

## gatsby-node.jsのTS化

次にgatsby-node.jsをTS化していくのでts-nodeをインストール

```
yarn add -D ts-node
```

gatsby-node.jsのTS化においては、gatsby-node.jsのファイル名はそのままにして、別にTS用のディレクトリとファイルである gatsby-node/index.ts を親ディレクトリ上に作ってここから引っ張ってきました。
参考：<a href="https://zenn.dev/ryo_kawamata/articles/gatsby-ts-2020">Gatsby.jsのTypeScript化 2020</a>

まずはindex.ts上にgatsby-node.jsと同じ処理をTypeScript化させます。

```gatsby-node/index.ts
import path from 'path'
import { createFilePath } from "gatsby-source-filesystem"
import { GatsbyNode } from 'gatsby'

export const onCreateNode: GatsbyNode["onCreateNode"] = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const slug = createFilePath({ node, getNode, basePath: `pages` })
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    })
  }
}

export const createPages: GatsbyNode["createPages"] = async ({ graphql, actions }) => {
  const { createPage } = actions
  const result = await graphql<{ allMarkdownRemark: GatsbyTypes.Query["allMarkdownRemark"]}>(`
  {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
  }
  `)
  const { data } = result || 'undefined';
  if( data === undefined) throw 'データが見つかりませんでした';

  data.allMarkdownRemark.edges.forEach(({node}) => {
    if(node.fields){
    createPage({
      path: node.fields.slug || '/undefined',
      component: path.resolve(`./src/templates/blog-post.tsx`),
      context: {
        slug: node.fields.slug
      }
    })
  }
  })
}
```


一方でgatsby-node.jsは上のgatsby-node/index.tsを引っ張ってくるコードに書き換える。

```gatsby-node.js
"use strict"

require("ts-node").register({
  compilerOptions: {
    module: "commonjs",
    target: "esnext",
  },
})

require("./src/__generated__/gatsby-types")

const {
  createPages,
  onCreateNode,
} = require("./gatsby-node/index")

exports.createPages = createPages
exports.onCreateNode = onCreateNode
```

この時点で一度 gatsby develop で動くか確認すると一応、トランスパイルはうまくいっていると思う（エラーなどあれば any などでごまかしましょうw）


## strictモードで手直ししていく

```tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": ["dom", "es2017"],                             /* Specify library files to be included in the compilation. */
    "jsx": "react",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    "outDir": "./build",                        /* Redirect output structure to the directory. */
    "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    "noUnusedLocals": true,                /* Report errors on unused locals. */
    "noUnusedParameters": true,            /* Report errors on unused parameters. */
    "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
    "baseUrl": "src",                       /* Base directory to resolve non-absolute module names. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
    "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
    "strict": true,                           /* Enable all strict type-checking options. */
    "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    "strictNullChecks": true,              /* Enable strict null checks. */
    "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
  },
  "include": ["src/**/*", "gatsby-node/index.ts"],
  "exclude": ["node_modules", "public", "build", "src/templates/blog-post.tsx"],
}

```

するとすぐにエラーが生じるので手直ししていきます。

## 生成されたundefined のところでエラーが生じるので修正する

gatsby-plugin-typegenで生成された型だと、Objectに'undefined'の可能性があるため型を拡張するかもしくは ? を挿入、もしくは if文 で 'undefined' の怒られを回避していく必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651103/ce22e684-e8af-154f-0802-ddcd7402259d.png)


### コンポーネントとpagesの手直して見た
gitはこちら - <a href="https://github.com/yoshisansan/gatsby-typescript-tutorial/tree/main">チュートリアルをTS化したブランチ(完成形)はココ</a>

```components/layout.tsx
/** @jsx jsx */
import { FC } from "react"
import { jsx, css } from "@emotion/react"
import { useStaticQuery, Link, graphql } from "gatsby"
import { rhythm } from "../utils/typography"

const Layout: FC = ({ children }) => {
  const data = useStaticQuery<GatsbyTypes.LayoutSiteMetadataQuery>(
    graphql`
    query LayoutSiteMetadata {
      site {
        siteMetadata {
          title
        }
      }
    }
    `
  )
  return (
    <div
      css={css`
        margin: 0 auto;
        max-width: 700px;
        padding: ${rhythm(2)};
        padding-top: ${rhythm(1.5)};
      `}
    >
      <Link to={`/`}>
        <h3
          css={css`
            margin-bottom: ${rhythm(2)};
            display: inline-block;
            font-style: normal;
          `}
        >
          {data.site?.siteMetadata?.title}
        </h3>
      </Link>
      <Link
        to={`/about/`}
        css={css`
          float: right;
        `}
      >
        About
      </Link>
      {children}
    </div>
  )
}

export default Layout
```

```components/seo.tsx
import React from "react"
// import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

interface SEOTypes {
  description?: string,
  lang?: string,
  meta?: any,
  title: string,
}

const SEO = ({
  description,
  lang,
  meta,
  title
}: SEOTypes) => {
  const { site } = useStaticQuery<GatsbyTypes.SEOsiteMetadataQuery>(
    graphql`
      query SEOsiteMetadata {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )

  const metaDescription = description || site?.siteMetadata?.description
  if(!lang) lang = 'ja';
  if(!meta) meta = {};

  return (
    <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={`%s | ${site?.siteMetadata?.title}`}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: site?.siteMetadata?.author,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
      ].concat(meta)}
    />
  )
}
export default SEO
```

```pages/index.tsx
/** @jsx jsx */
import {FC} from "react"
import { jsx, css } from '@emotion/react'
import { Link, graphql, PageProps } from "gatsby"
import { rhythm } from "../utils/typography"
import Layout from "../components/layout"

const Home: FC<PageProps<GatsbyTypes.MarkdownOfIndexQuery>> = ({data}) => {
  return (
    <Layout>
      <div>
        <h1
          css={css`
            display: inline-block;
            border-bottom: 1px solid;
          `}
        >
          Amazing Pandas Eating Things
        </h1>
        <h4>{data.allMarkdownRemark.totalCount} Posts</h4>
        {data.allMarkdownRemark.edges.map(({ node }) => (
          <div key={node.id}>
            <Link
              to={node.fields?.slug || '/'}
              css={css`
                text-decoration: none;
                color: inherit;
              `}
            >
            <h3
              css={css`
                margin-bottom: ${rhythm(1 / 4)};
              `}
            >
              {node.frontmatter?.title}{" "}
              <span
                css={css`
                  color: #bbb;
                `}
              >
                — {node.frontmatter?.date}
              </span>
            </h3>
            <p>{node.excerpt}</p>
            </Link>
          </div>
        ))}
      </div>
    </Layout>
  )
}

export const query = graphql`
  query MarkdownOfIndex {
    allMarkdownRemark {
      totalCount
      edges {
        node {
          id
          frontmatter {
            title
            date(formatString: "DD MMMM, YYYY")
          }
          fields {
            slug
          }
          excerpt
        }
      }
    }
  }
`

export default Home
```

```pages/about.tsx
import React, {FC} from "react"
import { graphql, PageProps } from "gatsby"
import Layout from "../components/layout"

const About: FC<PageProps<GatsbyTypes.AboutsiteMetadataQuery>> = ({ data }) => {
  return (
    <Layout>
      <h1>About {data.site?.siteMetadata?.title}</h1>
      <p>
        We are the only site running on your computer dedicated to showing the
        best photos and videos of pandas eating lots of food.
      </p>
    </Layout>
  )
}

export const query = graphql`
  query AboutsiteMetadata {
    site {
      siteMetadata {
        title
      }
    }
  }
`

export default About

```

```pages/my-files.tsx
import React, {FC} from "react"
import { graphql, PageProps } from "gatsby"
import Layout from "../components/layout"

const MyFiles: FC<PageProps<GatsbyTypes.myFilesAllFileQuery>> = ({data}) => {
  return (
    <Layout>
      <div>
        <h1>My Sites Files</h1>
        <table>
          <thead>
            <tr>
              <th>relativePath</th>
              <th>prettySize</th>
              <th>extension</th>
              <th>birthTime</th>
            </tr>
          </thead>
          <tbody>
            {data.allFile.edges.map(({ node }, index) => (
              <tr key={index}>
                <td>{node.relativePath}</td>
                <td>{node.prettySize}</td>
                <td>{node.extension}</td>
                <td>{node.birthTime}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query myFilesAllFile {
    allFile {
      edges {
        node {
          relativePath
          prettySize
          extension
          birthTime(fromNow: true)
        }
      }
    }
  }
`

export default MyFiles
```

```templates/blog-post.tsx
import React, {FC} from "react"
import { graphql, PageProps } from 'gatsby'
import Layout from "../components/layout"
import SEO from "../components/seo"

const BlogPost: FC<PageProps<GatsbyTypes.blogPostRemarkQuery>> = ({data}) => {
  const post = data.markdownRemark
  return (
    <Layout>
      <SEO title={post?.frontmatter?.title || "undefined"} description={post?.excerpt || "undefined"} />
      <div>
        <h1>{post?.frontmatter?.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post?.html || "undefined"}}/>
      </div>
    </Layout>
  )
}

export const query = graphql`
  query blogPostRemark($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
      excerpt
    }
  }
`

export default BlogPost

```

### 足りないライブラリを追加する

```
yarn add -D @types/react-helmet @types/typography
```

ここで、悲しいことにGatsbyJSのチュートリアルで使用されいてる"typography-theme-kirkham"の@types~が探しても見つからなかったのでTS化できなかった。

### developするとエラーが起こる

ここで、gatsby developで動作確認したいところだが、理由は分からないですが "gatsby-plugin-typegen"のプラグインに入ったままだと "コンポーネント側で生成されたGraphQLの型が消えてなくなり、永遠にターミナル上でReloadされ続けて動かせなくなってしまいました。"

（理由は分からないのでわかる方がいたら教えて欲しいです。developの処理の時だけuseStaticQueryのGraphQLを認識せずに型が再生成されるから？なのかも。）

消えてしまったコンポーネントのGraphQLの型は gatsby build で再ビルドすれば元に戻ります。
そのため、gatsby develop で動作確認する際には gatsby-config.jsで設定した gatsby-plugin-typegen をコメントアウトする必要がありました。

## ビルドしてserveしてみた

```
gatsby build
gatsby serve
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651103/2ed64012-a8a0-97be-5417-980bedb79f58.png)

http://localhost:9000/ で立ち上げることができれば完成。