生成されたJsファイルをTsファイルに変換する

はじめに

ここでは、JavaScript で記述されているファイルを TypeScript に変える方法を記述します。
スターターで生成された JavaScript ファイルを順次 TypeScript に変換してゆきます。
流れとしては、以下になります。

  1. ファイルを選択し拡張子を jsx から tsx に変更
  2. TypeScript の記述に変更
  3. 適宜コンパイルを行いエラー箇所を特定し修正
  4. 他のファイルでエラーが起こっていたら追って修正
  5. 上記をエラーが無くなるまで繰り返す

変換

主な手順

変換は以下のように行います。

G start toTsx 対象となるファイルの拡張子をtsxに変更 start->toTsx stop nonArg 関数の型にReact.FCを指定する toTsx->nonArg 引数なし defInterface 引数の型を定義(Ex.Props) toTsx->defInterface 引数あり typeQuery GraphQLのクエリがあれば結果の型定義 nonArg->typeQuery withArg 関数の型にReact.FC<Props>を指定する defInterface->withArg withArg->typeQuery typeDefs ローカルに定義されている関数や変数があれば型をつける typeQuery->typeDefs check 適宜"yarn tsc"で確認 typeDefs->check check->stop

index.jsの変換

index.js から index.tsx に変更します。
React をファンクションコンポーネントで構成することにし、型情報を付け加えます。

index.js
const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
  </Layout>
)

export default IndexPage

上記で言えば、引数のないファンクションコンポーネントなので、React.FC を使用します。
必要な変更はこれだけです。
変更箇所をハイライトします。

index.tsx
const IndexPage: React.FC = () => (  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
  </Layout>
);

export default IndexPage;

エラーを見ながら import 先を順次変換していきます。

layout.jsの変換

ここでのポイントは以下になります。

  1. 引数の型定義を行う
  2. 引数付きの関数コンポーネントに変換する
  3. propTypes は削除する
  4. GraphQL のクエリ結果の変数の型定義を行う

もとのコードは以下です。
変更が必要な箇所をハイライトします。

layout.js
const Layout = ({ children }) => {const data = useStaticQuery(graphql`  query SiteTitleQuery {
    site {
      siteMetadata {
        title
      }
    }
  }
`)

return (
  <>
    <Header siteTitle={data.site.siteMetadata.title} />
    <div
      style={{
        margin: `0 auto`,
        maxWidth: 960,
        padding: `0px 1.0875rem 1.45rem`,
        paddingTop: 0,
      }}
    >
      <main>{children}</main>
      <footer>
        © {new Date().getFullYear()}, Built with
        {` `}
        <a href="https://www.gatsbyjs.org">Gatsby</a>
      </footer>
    </div>
  </>
)
}

Layout.propTypes = {children: PropTypes.node.isRequired,}
export default Layout

これを変換してゆきます。

引数の型定義を行う

引数には children が存在し、propTypes を参考にしながらこの型を ReactNode とします。

interface Prop {
  children: React.ReactNode;
}

引数付きの関数コンポーネントに変換する

const Layout の部分は以下のように変更します。

- const Layout = ({ children }) => {
+ const Layout: React.FC<Prop> = ({ children }) => {

propTypesは削除する

propTypes は使用しないので、削除します。

-  Layout.propTypes = {
-  children: PropTypes.node.isRequired,
-  }

GraphQLのクエリ結果の型定義を行う

クエリ結果のデータについての型定義を行います。 実際にアクセスしているのが以下の部分です。

data.site.siteMetadata.title

上記から以下のように定義します。

interface QueryType {
  site: {
    siteMetadata : {
      title: string;
    }
  }
}
layout.tsx
interface Prop {
  children: React.ReactNode;
}

interface QueryType {
  site: {
    siteMetadata : {
      title: string;
    }
  }
}

const Layout: React.FC<Prop> = ({ children }) => {
  const data: QueryType = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `);

  return (
    <>
      <Header siteTitle={data.site.siteMetadata.title} />
      <div
        style={{
          margin: `0 auto`,
          maxWidth: 960,
          padding: `0px 1.0875rem 1.45rem`,
          paddingTop: 0,
        }}
      >
        <main>{children}</main>
        <footer>
          © {new Date().getFullYear()}, Built with
          {` `}
          <a href="https://www.gatsbyjs.org">Gatsby</a>
        </footer>
      </div>
    </>
  );
};

export default Layout;

header.js

layout.js 同じようにして header.js も変換します。 siteTitle の型は、propTypes を参考にして定義します。

image.js

今までのやりかたと、ほとんど同じようにして変換します。 QueryType に関しては、以下のようにしました。

interface QueryType {
  placeholderImage: {
    childImageSharp: {
      fluid: FluidObject;
    }
  }
}

seo.js

今までのものより少々複雑です。
変換元のファイルは以下のようになっています。

function SEO({ description, lang, meta, title }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        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)}
    />
  )
}

propTypes 等や上記ソースコードを参考にしながら、型をつけてゆきました。
最終的には以下のようになりました。

interface MetaObject {
  name: string;
  content: string;
}

interface Prop {
  description?: string;
  lang?: string;
  meta?: MetaObject[];
  title: string;
}

interface QueryType {
  site: {
    siteMetadata: {
      title: string;
      description: string;
      author: string;
    }
  }
}

const SEO: React.FC<Prop> =  ({ description = '', lang = 'en', meta = [], title }) => {
  const { site }: QueryType = useStaticQuery(
    graphql`
      query {
        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)}
    />
  );
};

export default SEO;

その他のファイル

以下のファイルに関しても同じようにして、変換を行いました。

  • 404.js
  • page-2.js

ここでは、特に新しいことは出てきませんでした。

おわりに

src 以下の全ての js ファイルを tsx ファイルに変換したら終了です。
今回変換したファイルは、実際には使用しない可能性が高いです。 ですが、流れを掴むために行ってみました。 とりあえず初期のプロジェクト作成に関してはこれで終了です。