GraphQL中的代数数据类型

Bir*_*sky 6 algebraic-data-types graphql graphql-js

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

// TypeScript
type SomeAlgebraicDataType = 
  | { state: 'A', subState1: string }
  | { state: 'B', subState2: string, subState3: number }
  | { state: 'C', subState4: string, subState5: string, subState6: number }
Run Code Online (Sandbox Code Playgroud)

注意如何基于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 }
Run Code Online (Sandbox Code Playgroud)

Dan*_*den 2

根据规范

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

联合(和接口)在规范中被称为抽象类型,因为返回联合的字段的具体(即实际)类型直到运行时才知道。然而,联合也符合代数类型的一般定义,因为它们实际上是两个或多个对象类型的总和。

问题中的伪代码与工作示例相距不远:

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 }
Run Code Online (Sandbox Code Playgroud)

主要区别在于,当我们使用 SDL 定义模式时,我们必须指定如何与类型定义分开解析 Union。如果您使用apollo-serveror makeExecutableSchemafrom graphql-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}`
          )
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用的是普通 GraphQL.js,则会将相同的函数作为参数提供给 Union 的构造函数resolveType

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