將 Gatsby 項目遷移到 TypeScript

示例倉庫 https://github.com/XYShaoKang...javascript

以前花了些時間將 gatsby-theme-gitbook遷移到 Typescript,以得到在 VSCode 中更好的編程體驗.

總體差很少已經完成遷移,剩下將 Gatsby 的 API 文件也遷移到 TS,這裏能夠看到 gatsby#21995 官方也在將核心代碼庫遷移到 Typescript,準備等待官方將核心代碼庫遷移完成,在遷移 API 文件.html

這篇文章用XYShaoKang/gatsby-project-config,演示如何將 gatsby 遷移到 TypeScript,但願能幫到一樣想要在 Gatsby 中使用 TS 的同窗.java

遷移步驟:node

  • TS 配置
  • 配置 ESLint 支持 TS
  • 使用gatsby-plugin-codegen完善 GraphQL 類型提示

初始化項目

gatsby new gatsby-migrate-to-typescript XYShaoKang/gatsby-project-config
cd gatsby-migrate-to-typescript
yarn develop

TS 配置

  • 安裝typescript
  • 添加typescript.json配置文件
  • 修改 js 文件爲 tsx
  • 補全 TS 聲明定義

安裝typescript

yarn add -D typescript

添加配置文件tsconfig.json

// https://www.typescriptlang.org/v2/docs/handbook/tsconfig-json.html
{
  "compilerOptions": {
    "target": "esnext", // 編譯生成的目標 es 版本,能夠根據須要設置
    "module": "commonjs", // 編譯生成的目標模塊系統
    "lib": ["dom", "es2015", "es2017"], // 配置須要包含的運行環境的類型定義
    "jsx": "react", // 配置 .tsx 文件的輸出模式
    "strict": true, // 開啓嚴格模式
    "esModuleInterop": true, // 兼容 CommonJS 和 ES Module
    "noUnusedLocals": true, // 報告未使用的局部變量的錯誤
    "noUnusedParameters": true, // 報告有關函數中未使用參數的錯誤
    "experimentalDecorators": true, // 啓用裝飾器
    "emitDecoratorMetadata": true, // 支持裝飾器上生成元數據,用來進行反射之類的操做
    "noEmit": true, // 不輸出 js,源映射或聲明之類的文件,單純用來檢查錯誤
    "skipLibCheck": true // 跳過聲明文件的類型檢查,只會檢查已引用的部分
  },
  "exclude": ["./node_modules", "./public", "./.cache"], // 解析時,應該跳過的路晉
  "include": ["src"] // 定義包含的路徑,定義在其中的聲明文件都會被解析進 vscode 的智能提示
}

index.js改爲index.tsx,從新啓動服務,查看效果.react

其實 Gatsby 內置了 支持 TS,不用其餘配置,只要把 index.js改爲 index.tsx就能夠直接運行.添加 TS 依賴是爲了顯示管理 TS,而 tsconfig.json也是這個目的,當咱們有須要新的特性以及自定義配置時,能夠手動添加.

補全 TS 聲明定義

打開index.tsx,VSCode 會報兩個錯誤,一個是找不到styled-components的聲明文件,這個能夠經過安裝@types/styled-components來解決.
另一個錯誤綁定元素「data」隱式具備「any」類型。,這個錯誤是由於咱們在tsconfig.json中指定了"strict": true,這會開啓嚴格的類型檢查,能夠經過關閉這個選項來解決,只是咱們用 TS 就是要用它的類型檢查的,因此正確的作法是給data定義類型.
下面來一一修復錯誤.git

安裝styled-components的聲明文件github

yarn add -D @types/styled-components

修改index.tsxweb

import React, { FC } from 'react'

// ...

interface PageQuery {
  data: {
    allMarkdownRemark: {
      edges: Array<{
        node: {
          frontmatter: {
            title: string
          }
          excerpt: string
        }
      }>
    }
  }
}

const Home: FC<PageQuery> = ({ data }) => {
  const node = data.allMarkdownRemark.edges[0].node

  const title = node.frontmatter?.title
  const excerpt = node.excerpt

  return (
    <>
      <Title>{title}</Title>
      <Content>{excerpt}</Content>
    </>
  )
}

export default Home

// ...

這時候會出現一個新的錯誤,在excerpt: string處提示Parsing error: Unexpected token,這是由於 ESLint 還沒法識別 TS 的語法,下面來配置 ESLint 支持 TS.typescript

配置 ESLint 支持 TypeScript

安裝依賴編程

yarn add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin

配置.eslintrc.js

module.exports = {
  parser: `@typescript-eslint/parser`, // 將解析器從`babel-eslint`替換成`@typescript-eslint/parser`,用以解析 TS 代碼
  extends: [
    `google`,
    `eslint:recommended`,
    `plugin:@typescript-eslint/recommended`, // 使用 @typescript-eslint/eslint-plugin 推薦配置
    `plugin:react/recommended`,
    `prettier/@typescript-eslint`, // 禁用 @typescript-eslint/eslint-plugin 中與 prettier 衝突的規則
    `plugin:prettier/recommended`,
  ],
  plugins: [
    `@typescript-eslint`, // 處理 TS 語法規則
    `react`,
    `filenames`,
  ],
  // ...
}

.vscode/settings.json中添加配置,讓VSCode使用ESLint擴展格式化tstsx文件

// .vscode/settings.json
{
  "eslint.format.enable": true,
  "[javascript]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  },
  "[typescript]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  }
}

完善 GraphQL 類型提示

// index.tsx
import React, { FC } from 'react'
// ...
interface PageQuery {
  data: {
    allMarkdownRemark: {
      edges: Array<{
        node: {
          frontmatter: {
            title: string
          }
          excerpt: string
        }
      }>
    }
  }
}

const Home: FC<PageQuery> = ({ data }) => {
  // ...
}

export default Home

export const query = graphql`
  query {
    allMarkdownRemark {
      edges {
        node {
          frontmatter {
            title
          }
          excerpt
        }
      }
    }
  }
`

咱們看看index.tsx文件,會發現PropTypesquery結構很是相似,在Gatsby運行時,會把query查詢的結果做爲組件prop.data傳入組件,而PropTypes是用來約束prop存在的.因此其實PropTypes就是根據query寫出來的.

若是有依據query自動生成PropTypes的功能就太棒了.
另一個問題是在query中編寫GraphQL查詢時,並無類型約束,也沒有智能提示.

這兩個問題能夠經過gatsby-plugin-codegen擴展來解決.
gatsby-plugin-codegen會生成apollo.config.jsschema.json,配合vscode-apollo擴展,能夠提供GraphQL的類型約束和智能提示.
另外會自動根據query中的GraphQL查詢,生成 TS 類型,放在對應的tsx文件同級目錄下的__generated__文件夾,使用時只須要引入便可.

這裏要注意, Apollo不支持匿名查詢,須要使用命名查詢

另外須要運行Gatsby以後才能經過gatsby-plugin-codegen生成類型文件

下面是具體操做

安裝vscode-apollo擴展

在 VSCode 中按 Ctrl + P ( MAC 下: Cmd + P) 輸入如下命令,按回車安裝

ext install apollographql.vscode-apollo

安裝gatsby-plugin-codegen依賴

在項目中添加依賴

yarn add gatsby-plugin-codegen

配置gatsby-config.js

添加gatsby-plugin-codegen插件

// gatsby-config.js
module.exports = {
  plugins: [
    // ...
    {
      resolve: `gatsby-plugin-codegen`,
      options: {},
    },
  ],
}

從新運行Gatsby生成類型文件

yarn develop

若是出現如下錯誤,通常是由於沒有爲查詢命名的緣故,給查詢添加命名便可,另外配置正確的話,打開對應的文件,有匿名查詢,編輯器會有錯誤提示.

fix-anonymous-operations.png

這個命名以後會做爲生成的類型名.

修改index.tsx以使用生成的類型

gatsby-plugin-codegen插件會更具查詢生成對應的查詢名稱的類型,保存在對應tsx文件同級的__generated__目錄下.使用時引入便可.

import { HomeQuery } from './__generated__/HomeQuery' // 引入自動生成的類型
// ...

// interface PageQuery {
//   data: {
//     allMarkdownRemark: {
//       edges: Array<{
//         node: {
//           frontmatter: {
//             title: string
//           }
//           excerpt: string
//         }
//       }>
//     }
//   }
// }

interface PageQuery {
  data: HomeQuery // 替換以前手寫的類型
}

// ...

將自動生成的文件添加到.gitignore

apollo.config.js, schema.json, __generated__能經過運行時生成,因此能夠添加到 .gitignore中,不用提交到 git 中.固然若是有須要也能夠選擇提交到 git 中.
# Generated types by gatsby-plugin-codegen
__generated__
apollo.config.js
schema.json

擴展閱讀

相關文章
相關標籤/搜索