为了使Gatsby+TypeScript更加舒适,可以使用gatsby-plugin-graphql-codegen进行配置
在 Gatsby + TypeScript 的架构中,有一个名为gatsby-plugin-graphql-codegen的库,可以自动生成GraphQL的类型定义。
使用这个库,从GraphQL获取的数据将自动添加类型,从而使开发变得相当方便。但是,我遇到了一些问题,所以我将在文章中总结概述和解决方案。
顺便提一下,在搭建 Gatsby + TypeScript 环境方面,这篇文章非常有参考价值。
将 Gatsby.js 完全转为 TypeScript – Qiita
环境
-
- TypeScript 3.9.7
React 16.13.1
Gatsby 2.4.13
gatsby-plugin-graphql-codegen 2.7.1
這個結論
总的来说,在gatsby-config.js(.ts)中加载插件时,设置以下选项将使您感到快乐。
module.exports = {
plugins: [
// ...中略
{
resolve: 'gatsby-plugin-graphql-codegen',
options: {
codegenConfig: { maybeValue: 'T | undefined' }, // これを追加!
},
},
]
}
这是什么意思?
gatsby-plugin-graphql-codegen在从GraphQL查询生成类型时,将所有返回值都用名为Maybe的类型进行包装。
export type Maybe<T> = T | null;
由于从GraphQL获取的数据可能为null,因此这是一个有效的类型定义。
在TypeScript中,您可以使用Optional Chaining?.来安全地获取可为空的值。
例如,我最喜欢的颜色是蓝色。
以 Gatsby 的默认模板 gatsby-starter-default 为例,下面是一个使用 GraphQL 查询来加载图片的组件。
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
const Image = () => {
const data = useStaticQuery(graphql`
query {
placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
childImageSharp {
fluid(maxWidth: 300) {
...GatsbyImageSharpFluid
}
}
}
}
`)
return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
}
export default Image
如果将其转换为TypeScript,可以这样编写。
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
import { ImageQuery } from "../../graphql-types"
const Image: React.FC = () => {
const data = useStaticQuery<ImageQuery>(graphql`
query Image {
placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
childImageSharp {
fluid(maxWidth: 300) {
...GatsbyImageSharpFluid
}
}
}
}
`)
return <Img fluid={data.placeholderImage?.childImageSharp?.fluid} />
}
export default Image
gatsby-plugin-graphql-codegen会根据为query命名(如上所示的query Image)来自动生成graphql-types.ts中的类型定义,我们可以将其类型指定为useStaticQuery,这样data就会获得类型补全。对于Img组件,我们可以使用Optional Chainging来传递值,这样即使无法获取到图片,也不会导致运行时错误,而是会进行处理。
发生的事情
然而,根据tsconfig.json的设置,可能会发生类似于1以下的编译错误。
この呼び出しに一致するオーバーロードはありません。
2 中 1 のオーバーロード, '(props: Readonly<GatsbyImageProps>): GatsbyImage' により、次のエラーが発生しました。
型 'Pick<ImageSharpFluid, "base64" | "aspectRatio" | "src" | "srcSet" | "sizes"> | null | undefined' を
型 'FluidObject | FluidObject[] | undefined' に割り当てることはできません。
型 'null' を型 'FluidObject | FluidObject[] | undefined' に割り当てることはできません。
2 中 2 のオーバーロード, '(props: GatsbyImageProps, context?: any): GatsbyImage' により、次のエラーが発生しました。
型 'Pick<ImageSharpFluid, "base64" | "aspectRatio" | "src" | "srcSet" | "sizes"> | null | undefined' を
型 'FluidObject | FluidObject[] | undefined' に割り当てることはできません。
型 'null' を型 'FluidObject | FluidObject[] | undefined' に割り当てることはできません。
当我们提取要点时,它说“无法将null分配给undefined”。
gatsby-image组件的Img已经被定义为可以接受undefined,但似乎无法接受null。
正如之前所述,gatsby-plugin-graphql-codegen会使用Maybe = T | null来包装所有类型,因此data.placeholderImage?.childImageSharp?.fluid可能被判断为null。
要避免这种情况,可以按以下方式编写,即当为null时自动变为undefined。
const Image: React.FC = () => {
// ...略
return <Img fluid={data.placeholderImage?.childImageSharp?.fluid ?? undefined} />
}
export default Image
只是说实话,对于从GraphQL获取的所有数据来说,这样做相当困难。特别是在React组件中,可选的props定义经常是T | undefined,所以会经常发生从null到undefined的转换。
或许可以改变的那种形式 de
我們回到開頭的結論,當插件加載時,需記錄以下設置。
module.exports = {
plugins: [
// ...中略
{
resolve: 'gatsby-plugin-graphql-codegen',
options: {
codegenConfig: { maybeValue: 'T | undefined' }, // これを追加!
},
},
]
}
您可以通过 codegenConfig.maybeValue 来覆盖生成的 Maybe 定义。
通过将 T | null 替换为 T | undefined,可以将可空值统一为 undefined,从而只需使用简单的 Optional Chaining 来编写。
const Image: React.FC = () => {
// ...略
return <Img fluid={data.placeholderImage?.childImageSharp?.fluid} />
}
export default Image
改变类型定义没问题吗?
我认为,将null自动视为undefined并不会有问题。因为它只是改变了类型定义,并且至少对转码后的JavaScript没有影响。我认为,如果使用最新的库,null和undefined之间的区别不会引发重大问题,但我并没有检查所有情况。如果有问题,请谅解。
总结
gatsby-plugin-graphql-codegenの生成するnullに悩まされている方はcodegenConfig: { maybeValue: ‘T | undefined’ }を設定すると快適になるかもしれません