作为 Write for DOnations 计划的一部分,作者选择了 Diversity in Tech Fund 来接受捐赠。
介绍
TypeScript 是 JavaScript 的超集,它在构建时添加了可选的静态类型,从而减少了调试运行时错误。 多年来,它已发展成为 JavaScript 的强大替代品。 同时,Gatsby 已经成为一个有用的创建静态网站的前端框架。 TypeScript 的静态类型功能与 Gatsby 之类的静态站点生成器配合得很好,而且 Gatsby 内置了对 TypeScript 编码的支持。
在本教程中,您将使用 Gatsby 的内置功能为 TypeScript 配置 Gatsby 项目。 在本教程之后,您将学习如何将 TypeScript 集成到您的 Gatsby 项目中。
先决条件
- 您需要同时安装 Node 和 npm 才能运行开发环境并分别处理与 TypeScript 或 Gatsby 相关的包。 本教程使用 Node.js 版本 14.17.2 和 npm 版本 6.14.13 进行了测试。 要在 macOS 或 Ubuntu 18.04 上安装,请按照 如何在 macOS 上安装 Node.js 和创建本地开发环境中的步骤或 How 的 使用 PPA 部分安装在 Ubuntu 20.04 上安装 Node.js。
- 您需要在计算机上安装 Gatsby CLI 命令行工具。 要进行设置,请阅读 如何设置您的第一个 Gatsby 站点 的开头部分。 本教程已在 Gatsby v3.9.0 和 Gatsby CLI v3.9.0 上进行了测试。
- 您将需要足够的 JavaScript 知识,尤其是 ES6+ 语法,例如解构和导入/导出。 您可以在 Understanding Destructuring, Rest Parameters, and Spread Syntax in JavaScript 和 Understanding Modules and Import and Export Statements in JavaScript 中找到有关这些主题的更多信息。
- 此外,您需要在您的机器上安装 TypeScript。 为此,请参阅 官方 TypeScript 网站 。 如果您使用 Visual Studio Code 之外的编辑器,您可能需要执行一些额外的步骤,以确保 TypeScript 在构建时执行类型检查并显示任何错误。 例如,如果您使用 Atom,则需要安装 atom-typescript 包 才能获得真正的 TypeScript 体验。 如果您只想为您的项目下载 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.js
、gatsby-browser.js
和 gatsby-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.js
、layout.js
和 seo.js
和 src/pages
中的 index.js
更改为 header.tsx
、layout.tsx
、seo.tsx
和 index.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
。
接下来,创建一个具有两个属性的对象,compilerOptions
和 include
,填充以下代码:
[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(dom
、es2015
等)的指定类型定义数组。strict
- 当设置为true
时,这会在构建时启用 TypeScript 的类型检查能力。noEmit
- 由于 Gatsby 已经使用 Babel 将您的代码编译为可读的 JavaScript,因此您将此选项更改为true
以将 TypeScript 排除在外。isolatedModules
- 通过选择 Babel 作为编译器/转译器,您选择一次编译一个文件,这可能会在运行时导致潜在问题。 将此选项设置为true
允许 TypeScript 在您即将遇到此问题时发出警告。esModuleIterop
- 启用此选项允许您使用 CommonJS(您的设置module
)和 ES 模块(导入和导出自定义变量和函数)更好地协同工作并允许所有导入的命名空间对象。noUnusedLocals
和noUnusedParamters
- 启用这两个选项会禁用 TypeScript 在创建未使用的局部变量或参数时通常会报告的错误。removeComments
- 将此设置为false
(或根本不设置)允许在任何 TypeScript 文件转换为 JavaScript 后出现注释。
您可以通过访问 TypeScript 的 tsconfig
参考指南 了解有关这些不同选项的更多信息。
现在已经为 Gatsby 配置了 TypeScript,您将通过重构 src/components
和 src/pages
中的一些样板文件来完成 TypeScript 集成。
第 4 步 — 为 TypeScript 重构 seo.tsx
在这一步中,您将向 seo.tsx
文件添加一些 TypeScript 语法。 这一步深入讲解 TypeScript 的一些概念; 下一步将展示如何以更简洁的方式重构其他样板代码。
TypeScript 的一个特点是其语法的灵活性。 如果您不想明确地为变量添加类型,则不必这样做。 Gatsby 认为,在您的工作流程中采用 TypeScript“可以而且应该是渐进式的”,因此这一步将集中在三个核心 TypeScript 概念上:
- 基本类型
- 定义类型和接口
- 处理构建时错误
TypeScript 中的基本类型
TypeScript 支持 基本数据类型 ,包括:boolean
、number
和 string
。 与 JavaScript 相比,TypeScript 的主要语法差异是现在可以使用关联类型定义变量。
例如,以下代码块显示了如何使用突出显示的代码分配基本类型:
let num: number; num = 0 let str: string; str = "TypeScript & Gatsby" let typeScriptIsAwesome: boolean; typeScriptIsAwesome = true;
在此代码中,num
必须是 number
,str
必须是 string
,并且 typeScriptIsAwesome
必须是 [ X104X]。
现在您将检查 src/components
目录中的 seo.tsx
文件中的 defaultProps
和 propTypes
声明。 在编辑器中打开文件并查找以下突出显示的行:
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 的弱类型系统。 defaultProps
和 propTypes
使用导入的 PropsTypes
类显式声明。 例如,在 propTypes
对象的 meta
prop(或 alias)中,它的值是一个对象数组,每个对象本身就是 [ X152X] 组件。 一些道具被标记为必需(isRequired
),而另一些则不是,这意味着它们是可选的。
由于您使用的是 TypeScript,因此您将替换此打字系统。 继续删除 defaultProps
和 propTypes
(以及文件顶部 PropTypes
的 import
语句)。 您的文件将如下所示:
[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
来替换已删除的 defaultProps
和 propTypes
定义。
添加以下突出显示的行:
[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
接口中被定义为必需属性.
最后,您可以通过设置类型来修改 metaDescription
和 defaultTitle
常量声明,在本例中为 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.tsx
和 header.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.tsx
和 layout.tsx
类似,使用相同的 prop 名称将 Header.defaultProps
和 Header.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 系列中编码 。