模式和类型 

本页介绍了基于 GraphQL 类型系统的走私类型系统。

走私可以用来访问现有的基于 JSON 的 API,或者实现你自己的服务。

走私模式语言 

由于我们不想依赖于特定的编程语言语法,为了讨论走私模式,我们将扩展 GraphQL 的模式语言。

走私模式应该以文件扩展名 *.contra 保存。

记录类型和字段 

走私模式的最基本组成部分是记录类型,它只代表你可以从你的服务中获取的一种对象类型,以及它包含哪些字段。在走私模式语言中,我们可以这样表示它

package com.example
@target(Scala)

## Character represents the characters in Star Wars.
type Character {
  name: String!
  appearsIn: [com.example.Episode]!
}

让我们来回顾一下,以便我们能有一个共同的词汇

现在你知道了走私记录类型是什么样的,以及如何阅读走私模式语言的基础知识。

since 注释 

为了支持模式演变,走私记录中的字段可以声明添加它的版本

package com.example
@target(Scala)

type Greeting {
  value: String!
  x: Int @since("0.2.0")
}

这意味着 value 字段从一开始就存在 ("0.0.0"),但可选的 x 字段是从版本 "0.2.0" 开始添加的。走私将生成多个构造函数以维护二进制兼容性。

由于 Int 是可选的,因此 None 被用作 x 的默认值。要提供其他默认值,可以按如下方式编写

package com.example
@target(Scala)

type Greeting {
  value: String!
  x: Int = 0 @since("0.2.0")
  p: Person = { name: "Foo" } @since("0.2.0")
  z: Person = raw"Person(\"Foo\")"
}

注意,0 将自动包装为选项。

标量类型 

走私自带了一组默认的标量类型

你也可以使用 Java 和 Scala 类名,例如 java.io.File

如果你使用 java.io.File 等类名,你还需要提供类型如何序列化和反序列化的信息。

枚举类型 

枚举类型(也称为枚举)是一种特殊的标量类型,它被限制为一组特定的允许值。这可以让你

  1. 验证任何这种类型的参数是否都是允许的值之一。
  2. 通过类型系统传达一个字段将始终是一组有限值中的一个。

以下是枚举定义在走私模式语言中的样子

package com.example
@target(Scala)

## Star Wars trilogy.
enum Episode {
  NewHope
  Empire
  Jedi
}

这意味着,无论我们在模式中使用 Episode 类型,我们都期望它完全是 NewHopeEmpireJedi 中的一个。

必需类型 

记录类型和枚举是你在走私中可以定义的唯一类型。但是,当你在模式的其他部分使用类型时,你可以应用额外的类型修饰符,这些修饰符会影响这些值的验证。让我们看一个例子

package com.example
@target(Scala)

## Character represents the characters in Star Wars.
type Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
}

这里,我们使用了一个 String 类型,并在类型名称后添加了一个感叹号 ! 来将其标记为必需。

列表类型 

列表的工作方式类似:我们可以使用一个类型修饰符来将类型标记为列表,这表明此字段将返回该类型的列表。在模式语言中,这用方括号 [] 将类型括起来表示。

延迟类型 

延迟类型在字段第一次使用之前延迟其初始化。在模式语言中,这用关键字 lazy 表示。

接口 

与许多类型系统一样,走私支持接口。接口是一个抽象类型,它包含一个类型必须包含的特定字段集,才能实现该接口。

例如,你可以有一个接口 Character 来表示星球大战三部曲中的任何角色

package com.example
@target(Scala)

## Character represents the characters in Star Wars.
interface Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
}

这意味着,任何实现 Character 的类型都需要拥有这些确切的字段。

例如,以下是一些可能实现 Character 的类型

package com.example
@target(Scala)

type Human implements Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
  starships: [com.example.Starship]
  totalCredits: Int
}

type Droid implements Character {
  name: String!
  appearsIn: [com.example.Episode]!
  friends: lazy [com.example.Character]
  primaryFunction: String
}

你可以看到,这两种类型都拥有 Character 接口中的所有字段,但还引入了额外的字段 totalCreditsstarshipsprimaryFunction,这些字段特定于该特定类型的角色。

消息 

除了字段,接口还可以声明消息。

package com.example
@target(Scala)

## Starship represents the starships in Star Wars.
interface Starship {
  name: String!
  length(unit: com.example.LengthUnit): Double
}

这意味着,任何实现 Starship 的类型都需要拥有这些确切的字段和消息。

额外代码 

作为将 Scala 或 Java 代码注入到生成的代码中的一个安全机制,走私提供了特殊的注释标记。

## Example of an interface
interface IntfExample {
  field: Int

  #x // Some extra code

  #xinterface Interface1
  #xinterface Interface2

  #xtostring return "custom";

  #xcompanion // Some extra companion code

  #xcompanioninterface CompanionInterface1
  #xcompanioninterface CompanionInterface2
}