Thein's Logo

Adding TypeScript to Gatsby

Oct 10, 20203 mins read
banner post image

I have been using Gatsby for my project recently. As the project slowly progressing, I decided to add TypeScript into it. Good news is that Gatsby natively supports TypeScript out of the box. According to Gatsby's documentation, you can start using the power of TypeScript by changing file type from .js to .tsx. However, there is no type checking provided. In this article, I would like to share my experience on how I added type checking for my Gatsby project with TypeScript.

To keep things simple, I will use Gatsby Starter Default as example.

Add tsconfig.json

There are many recommended tsconfig.json over the internet. You can also brew your own according to your preferences. I use this tsconfig.json from Gatsby's repo.

{
  "include": ["./src/**/*"],
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "lib": ["dom", "es2017"],
    // "allowJs": true,
    // "checkJs": true,
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noEmit": true,
    "skipLibCheck": true
  }
}

Install additional libraries

I added a few libraries as dev dependencies which provide extra type checking supports.

npm install -D typescript @types/node @types/react @types/react-dom

Next, I added type checking script in package.json without emitting outputs files.

"type-check": "tsc --noEmit"

Resolve type errors

Now, npm run type-check. You will see type errors after running the command. This is because TypeScript does not know what types are produced by those imports. You may also notice that there is already an example of using-typescript.tsx file in src/pages/. Now, let's look into those type errors and resolve the errors one by one.

> gatsby-starter-default@0.1.0 type-check projects/gatsby-starter-default
> tsc --noEmit

src/pages/using-typescript.tsx:3:42 - error TS2792: Cannot find module 'gatsby'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?

3 import { PageProps, Link, graphql } from "gatsby"
                                           ~~~~~~~~

src/pages/using-typescript.tsx:5:20 - error TS7016: Could not find a declaration file for module '../components/layout'. 'projects/gatsby-starter-default/src/components/layout.js' implicitly has an 'any' type.

5 import Layout from "../components/layout"
                     ~~~~~~~~~~~~~~~~~~~~~~

src/pages/using-typescript.tsx:6:17 - error TS7016: Could not find a declaration file for module '../components/seo'. 'projects/gatsby-starter-default/src/components/seo.js' implicitly has an 'any' type.

6 import SEO from "../components/seo"
                  ~~~~~~~~~~~~~~~~~~~


Found 3 errors.

By checking node_modules/gatsby, Gatsby does comes with index.d.ts. So it has to do something with tsconfig.json configuration file. I added "moduleResolution": "node" to fix the issue.

{
  "include": ["./src/**/*"],
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext", // Alternatively you can use "commonjs"
    "lib": ["dom", "es2017"],
    // "allowJs": true,
    // "checkJs": true,
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noEmit": true,
    "skipLibCheck": true,
    "moduleResolution": "node"
  }
}

Rewriting files

Next, we need to rewrite seo.js and layout.js. Let's rewrite seo.js file first. I changed its file name to seo.tsx. Now you will see another type error in this file.

import { Helmet } from "react-helmet"
                        ~~~~~~~~~~~~~
Could not find a declaration file for module 'react-helmet'. 'projects/gatsby-starter-default/node_modules/react-helmet/lib/Helmet.js' implicitly has an 'any' type.
  Try `npm install @types/react-helmet` if it exists or add a new declaration (.d.ts) file containing `declare module 'react-helmet';`ts(7016)

The error message is very straight forward and provided with possible solution. This is similar issue with Gatsby module mentioned above. Since react-helmet library does not come with type declaration file, we need to install:

npm install -D @types/react-helmet

Let's add the types and its default props value. You can remove defaultProps and PropTypes since we are now using TypeScript

import React, { FC } from "react"
// ...
// ...

// Type declaration
interface SEOProps {
  description?: string
  lang?: string
  meta?: Array<any>
  title: string
}

// Add default props value
const SEO: FC<SEOProps> = ({
  description = "",
  lang = "en",
  meta = [],
  title = ""
}) => (
  // ...
)

// You can remove the following PropTypes
// SEO.defaultProps = {
//   lang: `en`,
//   meta: [],
//   description: ``,
// }

// SEO.propTypes = {
//   description: PropTypes.string,
//   lang: PropTypes.string,
//   meta: PropTypes.arrayOf(PropTypes.object),
//   title: PropTypes.string.isRequired,
// }

Now you get the idea and you can try rewriting layout.js by yourself as an exercise. Moreover, you can change other *.js files in the project.

Summary

We added type checking to the default Gatsby starter with TypeScript. For reference, I have pushed the code to Github repo. Happy building your Gatsby project with TypeScript.

More readings