如何使用TypeScript设置Gatsby项目

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Diversity in Tech Fund 来接受捐赠。

介绍

TypeScriptJavaScript 的超集,它在构建时添加了可选的静态类型,从而减少了调试运行时错误。 多年来,它已发展成为 JavaScript 的强大替代品。 同时,Gatsby 已经成为一个有用的创建静态网站的前端框架。 TypeScript 的静态类型功能与 Gatsby 之类的静态站点生成器配合得很好,而且 Gatsby 内置了对 TypeScript 编码的支持。

在本教程中,您将使用 Gatsby 的内置功能为 TypeScript 配置 Gatsby 项目。 在本教程之后,您将学习如何将 TypeScript 集成到您的 Gatsby 项目中。

先决条件

第 1 步 — 创建一个新的 Gatsby 站点并删除样板文件

首先,您将创建 Gatsby 站点并确保您可以运行服务器并查看该站点。 之后,您将删除一些未使用的样板文件和代码。 这将设置您的项目以便在以后的步骤中进行编辑。

打开计算机的控制台/终端并运行以下命令:

gatsby new gatsby-typescript-tutorial

这将需要几秒钟的时间来运行,因为它会为 Gatsby 站点设置必要的样板文件和文件夹。 完成后,cd进入项目目录:

cd gatsby-typescript-tutorial

要确保站点的开发环境可以正常启动,请运行以下命令:

gatsby develop

几秒钟后,您将在控制台中收到以下消息:

Output...
You can now view gatsby-starter-default in the browser.

  http://localhost:8000

通常,默认端口是 :8000,但您可以通过运行 gatsby develop -p another_number 来更改它。

转到您喜欢的浏览器并在地址栏中键入 http://localhost:8000 以查找该站点。 它看起来像这样:

注意: 插件 gatsby-plugin-sharp 版本 3.9.0 存在一个已知问题,在构建 Gatsby 站点时可能会导致以下错误:

Output ERROR

ENOENT: no such file or directory, open 'your_file_path/gatsby-typescript-tutorial/.cache/caches/gatsby-plugin-sharp/diskstore-f6dcddbf3c9007cd2587894f75b5cd62.json'

此错误的解决方法是将 gatsby-plugin-sharp 降级到版本 3.8.0。 为此,请打开您的 package.json 文件并进行以下突出显示的更改:

[gatsby-typescript-tutorial/package.json]
...
  "dependencies": {
    "gatsby": "^3.9.0",
    "gatsby-plugin-gatsby-cloud": "^2.9.0",
    "gatsby-plugin-image": "^1.9.0",
    "gatsby-plugin-manifest": "^3.9.0",
    "gatsby-plugin-offline": "^4.9.0",
    "gatsby-plugin-react-helmet": "^4.9.0",
    "gatsby-plugin-sharp": "3.8.0",
    "gatsby-source-filesystem": "^3.9.0",
    "gatsby-transformer-sharp": "^3.9.0",
    "prop-types": "^15.7.2",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-helmet": "^6.1.0"
  },
...

保存文件,然后使用以下命令重新安装依赖项:

npm install

现在使用 Gatsby CLI 删除 public.cache 文件夹,以便可以使用新的依赖项列表重新构建它们:

gatsby clean

您现在可以使用 gatsby develop 在本地开发服务器上启动 Gatsby 站点。 有关此解决方案的更多信息,请阅读 GitHub 内存线程错误:ENOENT:没有此类文件或目录 Gatsby 错误


接下来,您将删除所有不必要的文件。 这包括 gatsby-node.jsgatsby-browser.jsgatsby-ssr.js

rm gatsby-node.js
rm gatsby-browser.js
rm gatsby-ssr.js

接下来,要完成设置,您将从项目的索引页面中删除一些样板代码。 在项目的根目录中,前往 src 目录,然后是 pages,然后打开 index.js 文件。

对于本教程,您将只使用 <StaticImage /> 组件,因此您可以删除与 <Link /> 组件相关的代码,以及 h1 和 [ X166X] 元素。 您的文件将如下所示:

gatsby-typescript-tutorial/src/pages/index.js

import * as React from "react"
import { StaticImage } from "gatsby-plugin-image"

import Layout from "../components/layout"
import Seo from "../components/seo"

const IndexPage = () => (
  <Layout>
    <Seo title="Home" />
    <StaticImage
      src="../images/gatsby-astronaut.png"
      width={300}
      quality={95}
      formats={["AUTO", "WEBP", "AVIF"]}
      alt="A Gatsby astronaut"
      style={{ marginBottom: `1.45rem` }}
    />
  </Layout>
)

export default IndexPage

保存并关闭文件。

现在您已经创建了项目并完成了一些初始设置,您可以安装必要的插件了。

第 2 步 — 安装依赖项

为了在 Gatsby 中设置对 TypeScript 的支持,您需要一些额外的插件和依赖项,您将在此步骤中安装它们。

gatsby-plugin-typescript 插件已经附带了一个新创建的 Gatsby 站点。 除非您想更改其任何默认选项,否则您不必将此插件显式添加到您的 gatsby-config.js 文件中。 这个 Gatsby 插件使在 TypeScript 中编写 .ts.tsx 文件成为可能。

由于您的应用可以读取 TypeScript 文件,因此您现在将 Gatsby 的 JavaScript 文件更改为 TypeScript 文件扩展名。 特别是,将 src/components 中的 header.jslayout.jsseo.jssrc/pages 中的 index.js 更改为 header.tsxlayout.tsxseo.tsxindex.tsx

mv src/components/header.js src/components/header.tsx
mv src/components/layout.js src/components/layout.tsx
mv src/components/seo.js src/components/seo.tsx
mv src/pages/index.js src/pages/index.tsx

您正在使用 mv 命令将文件重命名为第二个参数。 .tsx 是文件扩展名,因为这些文件使用 JSX

然而,关于 gatsby-plugin-typescript 插件有一个重要的警告:它不包括构建时的类型检查(TypeScript 的核心功能)。 如果您使用的是 VS Code,这将不是问题,因为 TypeScript 是 Visual Studio 中支持的语言。 但是如果你使用另一个编辑器,比如 Atom,你需要做一些额外的配置来获得完整的 TypeScript 开发体验。

由于 Gatsby 是基于 React 的框架,因此还建议添加一些额外的 React 相关类型。 要为特定于 React 的类型添加类型检查,请运行以下命令:

npm add @types/react

要为与 React DOM 相关的类型添加类型检查,请使用以下命令:

npm add @types/react-dom

现在您已经熟悉了插件 gatsby-plugin-typescript,您可以在下一步中为 TypeScript 配置 Gatsby 站点了。

第 3 步 — 使用 tsconfig.json 文件为 Gatsby 配置 TypeScript

在此步骤中,您将创建一个 tsconfig.json 文件。 tsconfig.json 文件有两个主要用途:建立 TypeScript 项目的根目录 (include) 和覆盖 TypeScript 编译器的默认配置 (compilerOptions)。 有几种方法可以创建此文件。 如果你用 npm 安装了 tsc 命令行工具,你可以用 tsc --init 创建一个新的 tsconfig 文件。 但是该文件随后会填充许多默认选项和注释。

相反,在您的目录 (gatsby-typescript-project/) 的根目录下创建一个新文件并将其命名为 tsconfig.json

接下来,创建一个具有两个属性的对象,compilerOptionsinclude,填充以下代码:

[label gatsby-typescript-tutorial/tsconfig.json] 
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "jsx": "preserve",
    "lib": ["dom", "es2015", "es2017"],
    "strict": true,
    "noEmit": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "removeComments": false
  },
  "include": ["./src/**/*"]
}

注意:此配置部分基于gatsby-starter-typescript-plus启动器。


保存此文件并在完成后将其关闭。

include 属性指向一个 array 文件名或路径,编译器知道从 TypeScript 转换为 JavaScript。

以下是 compilerOptions 中使用的每个选项的简要说明:

  • module - 设置项目的模块系统; commonjs 默认使用。
  • target - 根据您使用的 JavaScript 版本,此选项决定了哪些功能需要降级,哪些功能不需要处理。 如果您的项目部署到较旧的环境而不是旧环境,这将很有帮助。 较新的环境。
  • jsx - 设置 JSX 在 .tsx 文件中的处理方式。 preserve 选项使 JSX 保持不变。
  • lib - 不同 JS 库/API(domes2015 等)的指定类型定义数组。
  • strict - 当设置为 true 时,这会在构建时启用 TypeScript 的类型检查能力。
  • noEmit - 由于 Gatsby 已经使用 Babel 将您的代码编译为可读的 JavaScript,因此您将此选项更改为 true 以将 TypeScript 排除在外。
  • isolatedModules - 通过选择 Babel 作为编译器/转译器,您选择一次编译一个文件,这可能会在运行时导致潜在问题。 将此选项设置为 true 允许 TypeScript 在您即将遇到此问题时发出警告。
  • esModuleIterop - 启用此选项允许您使用 CommonJS(您的设置 module)和 ES 模块(导入和导出自定义变量和函数)更好地协同工作并允许所有导入的命名空间对象。
  • noUnusedLocalsnoUnusedParamters - 启用这两个选项会禁用 TypeScript 在创建未使用的局部变量或参数时通常会报告的错误。
  • removeComments - 将此设置为 false(或根本不设置)允许在任何 TypeScript 文件转换为 JavaScript 后出现注释。

您可以通过访问 TypeScript 的 tsconfig 参考指南 了解有关这些不同选项的更多信息。

现在已经为 Gatsby 配置了 TypeScript,您将通过重构 src/componentssrc/pages 中的一些样板文件来完成 TypeScript 集成。

第 4 步 — 为 TypeScript 重构 seo.tsx

在这一步中,您将向 seo.tsx 文件添加一些 TypeScript 语法。 这一步深入讲解 TypeScript 的一些概念; 下一步将展示如何以更简洁的方式重构其他样板代码。

TypeScript 的一个特点是其语法的灵活性。 如果您不想明确地为变量添加类型,则不必这样做。 Gatsby 认为,在您的工作流程中采用 TypeScript“可以而且应该是渐进式的”,因此这一步将集中在三个核心 TypeScript 概念上:

  • 基本类型
  • 定义类型和接口
  • 处理构建时错误

TypeScript 中的基本类型

TypeScript 支持 基本数据类型 ,包括:booleannumberstring。 与 JavaScript 相比,TypeScript 的主要语法差异是现在可以使用关联类型定义变量。

例如,以下代码块显示了如何使用突出显示的代码分配基本类型:

let num: number;
num = 0

let str: string;
str = "TypeScript & Gatsby"

let typeScriptIsAwesome: boolean;
typeScriptIsAwesome = true;

在此代码中,num 必须是 numberstr 必须是 string,并且 typeScriptIsAwesome 必须是 [ X104X]。

现在您将检查 src/components 目录中的 seo.tsx 文件中的 defaultPropspropTypes 声明。 在编辑器中打开文件并查找以下突出显示的行:

gatsby-typescript-tutorial/src/components/seo.tsx

...
import * as React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

...
      ].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

默认情况下,Gatsby 网站的 SEO 组件带有使用 PropTypes 的弱类型系统。 defaultPropspropTypes 使用导入的 PropsTypes 类显式声明。 例如,在 propTypes 对象的 meta prop(或 alias)中,它的值是一个对象数组,每个对象本身就是 [ X152X] 组件。 一些道具被标记为必需(isRequired),而另一些则不是,这意味着它们是可选的。

由于您使用的是 TypeScript,因此您将替换此打字系统。 继续删除 defaultPropspropTypes(以及文件顶部 PropTypesimport 语句)。 您的文件将如下所示:

[label gatsby-typescript-tutorial/src/components/seo.tsx] 
...
import * as React from "react"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"


...
      ].concat(meta)}
    />
  )
}

export default SEO

现在您已经删除了默认类型,您将使用 TypeScript 写出类型别名。

定义 TypeScript 接口

在 TypeScript 中, 接口 用于定义自定义类型的“形状”。 这些用于表示复杂数据的值类型,例如 React 组件和函数参数。 在 seo.tsx 文件中,您将构建一个 interface 来替换已删除的 defaultPropspropTypes 定义。

添加以下突出显示的行:

[label gatsby-typescript-tutorial/src/components/seo.tsx] 
...
import * as React from "react"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

interface SEOProps {
  description?: string,
  lang?: string,
  meta?: Array<{name: string, content: string}>,
  title: string
}

...

接口 SEOProps 完成了 SEO.propTypes 所做的工作,方法是设置每个属性关联的数据类型,并根据需要使用 ? 字符标记一些属性。

键入函数

就像在 JavaScript 中一样,函数在 TypeScript 应用程序中发挥着重要作用。 您甚至可以通过指定传递给它们的参数的数据类型来键入函数。 在 seo.tsx 文件中,您现在将处理已定义的 SEO 函数组件。 在定义 SEOProps 接口的地方,您将显式声明 SEO 组件的函数参数的类型,以及 SEOProps 的返回类型:

添加以下突出显示的代码:

gatsby-typescript-tutorial/src/components/seo.tsx

...
interface SEOProps {
  description?: string,
  lang?: string,
  meta?: Array<{name: string, content: string}>,
  title: string
}

function SEO({ description='', lang='en', meta=[], title }: SEOProps) {
  ...
}

在这里,您为 SEO 函数参数设置默认值,以便它们遵守接口,并使用 : SEOProps 添加接口。 请记住,您至少必须将 title 包含在传递给 SEO 组件的参数列表中,因为它在您之前定义的 SEOProps 接口中被定义为必需属性.

最后,您可以通过设置类型来修改 metaDescriptiondefaultTitle 常量声明,在本例中为 string

[label gatsby-typescript-tutorial/src/components/seo.tsx] 
...
function SEO({ description='', lang='en', meta=[], title }: SEOProps) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )
  
  const metaDescription: string = description || site.siteMetadata.description
  const defaultTitle: string = site.siteMetadata?.title
...

TypeScript 中的另一种类型是 any 类型。 对于您正在处理类型不明确或难以定义的变量的情况,请使用 any 作为最后的手段,以避免任何构建时错误。

使用 any 类型的一个示例是在处理从第三方获取的数据时,例如 API 请求或 GraphQL 查询。 在 seo.tsx 文件中,其中解构的 site 属性是使用 GraphQL 静态查询定义的,将其类型设置为 any

gatsby-typescript-tutorial/src/components/seo.tsx

...
interface SEOProps {
  description?: string,
  lang?: string,
  meta?: Array<{name: string, content: string}>,
  title: string
}

function SEO({ description='', lang='en', meta=[], title }: Props) {
  const { site }: any = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )
  ...
}

保存并退出文件。

始终保持定义的值与其类型一致很重要。 否则,您将看到通过 TypeScript 编译器出现的构建时错误。

构建时错误

习惯 TypeScript 在构建时捕获和报告的错误会很有帮助。 这个想法是 TypeScript 在构建时捕获这些错误,主要是与类型相关的,从长远来看(在编译时),这减少了调试量。

发生构建时错误的一个示例是,当您声明一种类型的变量但为其分配了另一种类型的值时。 如果您要将传递给 SEO 组件的关键字参数之一的值更改为不同类型之一,TypeScript 编译器将检测到不一致并报告错误。 以下是 VSCode 中的图像:

错误显示 Type 'number' is not assignable to type 'string'。 这是因为,当您设置 interface 时,您说 description 属性的类型为 string。 值 0 的类型为 number。 如果将 description 的值改回“字符串”,错误消息将消失。

第 5 步 — 重构样板的其余部分

最后,您将使用 TypeScript 重构剩余的样板文件:layout.tsxheader.tsx。 与 seo.tsx 一样,这些组件文件位于 src/components 目录中。

打开 src/components/layout.tsx。 靠近底部的是定义的 Layout.propTypes。 删除以下突出显示的行:

[label gatsby-typescript-tutorial/src/components/layout.tsx] 
/**
 * Layout component that queries for data
 * with Gatsby's useStaticQuery component
 *
 * See: https://www.gatsbyjs.com/docs/use-static-query/
 */

import * as React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
...

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

export default Layout

children 属性显示它的值是 PropTypes 类的 node 类型。 另外,它是必需的道具。 由于布局中的 children 可以是从简单文本到 React 子组件的任何内容,因此通过在顶部附近导入并将其添加到界面中,使用 ReactNode 作为关联类型:

添加以下突出显示的行:

gatsby-typescript-tutorial/src/components/layout.tsx

...
import * as React from "react"
import { useStaticQuery, graphql } from "gatsby"

import Header from "./header"
import "./layout.css"

interface LayoutProps {
  children: ReactNode
}

const Layout = ({ children }: LayoutProps) => {
  ...

接下来,向 data 变量添加一个类型,该变量存储获取站点标题数据的 GraphQL 查询。 由于此查询对象来自 GraphQL 等第三方实体,因此将 data 指定为 any 类型。 最后,将 string 类型添加到使用该数据的 siteTitle 变量中:

[label gatsby-typescript-tutorial/src/components/layout.tsx] 
...
const Layout = ({ children }: LayoutProps) => {
  const data: any = useStaticQuery(graphql`
  query MyQuery {
    site {
      siteMetadata {
        title
      }
    }
  }
`)

const siteTitle: string = data.site.siteMetadata?.title || `Title`

  return (
    <>
      <Header siteTitle={siteTitle} />
      <div
...

保存并关闭文件。

现在打开 src/components/header.tsx 文件。 该文件还带有预定义的道具类型,使用 PropTypes 类。 与 seo.tsxlayout.tsx 类似,使用相同的 prop 名称将 Header.defaultPropsHeader.propTypes 替换为 interface

gatsby-typescript-tutorial/src/components/header.tsx

import * as React from "react"
import { Link } from "gatsby"

interface HeaderProps {
  siteTitle: string
}

const Header = ({ siteTitle }: HeaderProps) => (
  <header
    style={{
      background: `rebeccapurple`,
      marginBottom: `1.45rem`,
    }}
  >
    <div
      style={{
        margin: `0 auto`,
        maxWidth: 960,
        padding: `1.45rem 1.0875rem`,
      }}
    >
      <h1 style={{ margin: 0 }}>
        <Link
          to="/"
          style={{
            color: `white`,
            textDecoration: `none`,
          }}
        >
          {siteTitle}
        </Link>
      </h1>
    </div>
  </header>
)

export default Header

保存并关闭文件。

为 TypeScript 重构文件后,您现在可以重新启动服务器以确保一切正常。 运行以下命令:

gatsby develop

当您导航到 localhost:8000 时,您的浏览器将呈现以下内容:

结论

TypeScript 的静态类型功能在将调试保持在最低限度方面大有帮助。 它也是 Gatsby 网站的一种很好的语言,因为它默认受支持。 Gatsby 本身是一个有用的前端工具,用于创建静态站点,例如登录页面。

您现在可以使用两种流行的工具。 要了解有关 TypeScript 的更多信息以及您可以使用它做的所有事情,请访问我们的 如何在 TypeScript 系列中编码