在 gatsby 中将 typescript 标记的联合添加到 graphql 查询的结果

Posted

技术标签:

【中文标题】在 gatsby 中将 typescript 标记的联合添加到 graphql 查询的结果【英文标题】:Adding typescript tagged unions to result from graphql query in gatsby 【发布时间】:2020-10-10 16:52:25 【问题描述】:

我使用Typescript tagged unions 来检查函数参数使用的接口。这里有两个接口,每个接口都可以传入下面的func函数中

export interface IPubArticle 
  kind: "IPubArticle"
  node: 
    fields: 
      slug: string
    
    frontmatter: 
      title: string
      category: string
      author: string
      issue: string
      date: string
      givenRank: number
    
  


export interface IArticleSquare
  kind: "IArticleSquare"
  node: 
    fields: 
      slug: string
    
    frontmatter: 
      title: string
      subTitle: string
      category: string
      author: string
      featuredImage: 
        childImageSharp: IFluidObject
      
      date: string
      givenRank: number
    
  


const func = (data: data: IArticleSquare | IPubArticle) => 
    switch (data.kind)
        case "IArticleSquare":
            ...
        case "IPubArticle":
            ...
    

我现在遇到了一个问题,因为显然我的 graphql 调用与接口不匹配,因为缺少变量类型

Argument of type ' node:  fields:  slug: string; ; frontmatter:  title: string; subTitle: string; date: string; description: string; category: string; author: string; givenRank: number; featuredImage:  childImageSharp: GatsbyImageProps; ; ; ; []' is not assignable to parameter of type 'IArticleSquare[]'.
  Property 'kind' is missing in type ' node:  fields:  slug: string; ; frontmatter:  title: string; subTitle: string; date: string; description: string; category: string; author: string; givenRank: number; featuredImage:  childImageSharp: GatsbyImageProps; ; ; ; ' but required in type 'IArticleSquare'.ts(2345)
article.interface.ts(21, 3): 'kind' is declared here.

这是我的 graphql 查询

export const pageQuery = graphql`
  query($path: string!) 
    allMarkdownRemark(
      sort:  order: DESC, fields: [frontmatter___date] 
    ) 
      edges 
        node 
          fields 
            slug
          
          frontmatter 
            title
            subTitle
            date(formatstring: "MMMM DD, YYYY")
            category
            featuredImage 
              childImageSharp 
                fluid(maxWidth: 400) 
                  ...GatsbyImageSharpFluid
                
              
            
          
        
      
    

我该如何解决这个问题?我真的不知道该怎么办

【问题讨论】:

API/服务器响应根本不包含 kind 字段/属性,因此与您的定义不匹配...您应该使用基于服务器 defs/specs/typings 的类型...您可以通过featuredImage 识别这些类型 typescriptlang.org/docs/handbook/… 【参考方案1】:

假设IArticleSquareNodefrontmatter中总是有一个subtitleIArticleNode没有,你可以写一个User-Defined Type Guard(isSquareNode)来帮助你识别节点类型。

interface IFrontmatter 
  title: string
  category: string
  author: string
  issue: string
  date: string
  givenRank: number


interface IFrontmatterSquare extends IFrontmatter 
  subTitle: string


interface IFields 
  slug: string


interface IArticleNode 
  fields: IFields
  frontmatter: IFrontmatter


interface IArticleSquareNode 
  fields: IFields
  frontmatter: IFrontmatterSquare


function isSquareNode(
  node: IArticleSquareNode | IArticleNode,
): node is IArticleSquareNode 
  return (node as IArticleSquareNode).frontmatter.subTitle !== undefined


const func = (node: IArticleSquareNode | IArticleNode) => 
  if (isSquareNode(node)) 
    return 'is of type IArticleSquareNode'
   else 
    return 'is of type IArticleNode'
  


const testArticleNode = 
  fields: 
    slug: '/myslug/',
  ,
  frontmatter: 
    title: 'title',
    category: 'category',
    author: 'author',
    issue: 'issue',
    date: 'date',
    givenRank: 1,
  ,


const testArticleSquareNode = 
  fields: 
    slug: '/myslug/',
  ,
  frontmatter: 
    title: 'title',
    subTitle: 'subTitle',
    category: 'category',
    author: 'author',
    issue: 'issue',
    date: 'date',
    givenRank: 1,
  ,


console.log(func(testArticleNode)) // is of type IArticleNode
console.log(func(testArticleSquareNode)) // is of type IArticleSquareNode 

【讨论】:

所以前端是从 Markdown 中提取的。我注意到当降价文件中没有列出 subTitle 属性时,subTittle 属性会用空字符串"" 实例化。这算不算总是有子标题? 而且它必须使用类型保护?你不能使用标记工会? 如果您可以控制数据源,我想您可以使用标记联合。但鉴于您的问题,您似乎没有这样做。

以上是关于在 gatsby 中将 typescript 标记的联合添加到 graphql 查询的结果的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Gatsby 中将 markdown frontmatter 显示为 HTML

使用 gatsby-plugin-categories / gatsby-plugin-tags 标记云和类别列表

在 Gatsby 应用程序中检测到多个全局站点标记 (gtag.js) 安装

连接两个 gatsby 节点

Gatsby - WebpackError: Invariant Violation error with apolloClient

如何在 TypeScript 中将 Set 转换为数组