GraphQL中的代数数据类型

时间:2017-12-15 22:49:40

标签: typescript graphql graphql-js algebraic-data-types

是否有一种技术可以让我在GraphQL中声明这样的ADT?

// TypeScript
type SomeAlgebraicDataType = 
  | { state: 'A', subState1: string }
  | { state: 'B', subState2: string, subState3: number }
  | { state: 'C', subState4: string, subState5: string, subState6: number }

请注意,基于state鉴别器,可以推断出结构的其余部分。

这是一些说明这个想法的伪代码:

union AlgebraicDataType = StateA | StateB | StateC

type StateA {state: StateDiscriminator.A, subState1: String }
type StateB {state: StateDiscriminator.B, subState2: String, subState3: Int }
type StateC {state: StateDiscriminator.C, subState4: String, subState5: String, subState6: Int}

enum StateDiscriminator { A B C }

1 个答案:

答案 0 :(得分:1)

根据spec

  

GraphQL并集表示一个对象,该对象可以是GraphQL对象类型的列表之一,但在这些类型之间不提供任何保证的字段。它们与接口的不同之处还在于,对象类型声明它们实现的接口,但不知道包含它们的联合。

规范中将

Union(和接口)称为抽象类型,因为返回Union的字段的具体(即实际)类型直到运行时才知道。但是,并集也符合algebraic type的一般定义,因为它们实际上是两个或多个对象类型的总和。

问题中的伪代码离工作示例不远:

union AlgebraicDataType = StateA | StateB | StateC

type StateA {state: StateDiscriminator, subState1: String }
type StateB {state: StateDiscriminator, subState2: String, subState3: Int }
type StateC {state: StateDiscriminator, subState4: String, subState5: String, subState6: Int}

enum StateDiscriminator { A B C }

主要区别在于,当我们使用SDL定义架构时,我们必须指定如何与类型定义分开解析联合。如果您使用的是apollo-server中的makeExecutableSchemagraphql-tools,我们会将此逻辑指定为解析器映射的一部分:

const resolvers = {
  AlgebraicDataType: {
    __resolveType: (obj) => {
      switch (obj.state) {
        case 'A': return 'StateA'
        case 'B': return 'StateB'
        case 'C': return 'StateC'
        default: {
          throw new TypeError(
            `Unknown state for AlgebraicDataType: ${obj.state}`
          )
        }
      }
    }
  }
}

如果您使用香草GraphQL.js,则将相同的函数作为resolveType参数提供给Union的构造函数。

还值得注意的是,如果未提供resolveType函数,默认情况下GraphQL将在提供的对象上查找名为__typename的属性,并使用该属性来解析类型。因此,只要您在解析器中返回具有该属性的对象,就可以完全省略该函数。