类型系统详解:标量、对象、枚举、接口、联合与输入类型


类型系统详解:标量、对象、枚举、接口、联合与输入类型

标量类型:值的原子

标量是GraphQL类型系统的最底层——它们是不可再分的值。GraphQL内置了五个标量:

标量 说明 示例值
Int 32位整数 42
Float 浮点数 3.14
String UTF-8字符串 "hello"
Boolean 布尔值 true
ID 序列化字符串,唯一标识 "user_1001"

除了内置标量,你可以定义自定义标量。自定义标量需要在Schema中声明,并在服务端代码中实现序列化/反序列化逻辑:

scalar DateTime scalar Email scalar URL scalar JSON scalar Currency ​

自定义标量的实际用途是提供运行时验证。比如 Email 标量在输入时验证格式、输出时序列化为字符串;DateTime 标量在输入时解析ISO 8601字符串、输出时格式化为指定格式。这比在每个字段上手动验证要系统化得多。

// Apollo Server 自定义标量实现 const GraphQLDateTime = new GraphQLScalarType({ name: 'DateTime', serialize: (value) => value.toISOString(), parseValue: (value) => new Date(value), parseLiteral: (ast) => { if (ast.kind === Kind.STRING) return new Date(ast.value); throw new Error(`DateTime must be a string, got ${ast.kind}`); } }); ​

对象类型:数据的骨架

对象类型是GraphQL的核心构造。它定义一组命名字段,每个字段有自己的类型:

type User { id: ID! name: String! email: String! avatarUrl: String role: UserRole! posts(limit: Int = 10, offset: Int = 0): [Post!]! followers: [User!]! settings: UserSettings } ​

对象类型的字段类型可以是标量、其他对象类型、列表([Type])、或者非空约束(Type!)。! 是非空标记——它表示查询执行时该字段保证有值。如果解析器返回了 null,GraphQL引擎会在该位置插入 null 并继续执行(不会抛异常,因为GraphQL的错误处理是部分执行的)。

列表类型的组合规则:

  • [String] — 可能为null的列表,列表元素也可能为null
  • [String!] — 可能为null的列表,但列表中每个元素保证非null
  • [String]! — 列表本身保证非null,但元素可能为null
  • [String!]! — 列表和元素都保证非null

枚举类型:有限的选项集

枚举用于定义一组有限的、预定义的值。它让API的约束显式化——客户端不需要猜某个字符串字段接受什么值,枚举本身列出了所有合法选项。

enum UserRole { ADMIN EDITOR VIEWER GUEST } enum PostStatus { DRAFT PUBLISHED ARCHIVED } enum SortDirection { ASC DESC } type Query { users(role: UserRole, sortBy: SortDirection): [User!]! posts(status: PostStatus): [Post!]! } ​

枚举在Schema中定义,在查询中使用枚举值(不加引号):

{ users(role: ADMIN, sortBy: ASC) { name email } } ​

枚举值在序列化时是字符串,但GraphQL的类型系统确保只有预定义的值能通过验证。

接口类型:共享契约

接口定义了一组共享字段,多个对象类型可以实现(implements)同一个接口。它是GraphQL中实现多态和代码复用的核心机制。

interface Node { id: ID! } interface Timestamped { createdAt: DateTime! updatedAt: DateTime! } interface Commentable { comments(limit: Int = 10): [Comment!]! commentsCount: Int! } # 多个类型实现同一接口 type Post implements Node & Timestamped & Commentable { id: ID! title: String! content: String! createdAt: DateTime! updatedAt: DateTime! comments(limit: Int): [Comment!]! commentsCount: Int! } type Video implements Node & Timestamped & Commentable { id: ID! url: String! duration: Int! thumbnailUrl: String! createdAt: DateTime! updatedAt: DateTime! comments(limit: Int): [Comment!]! commentsCount: Int! } type Comment implements Node & Timestamped { id: ID! body: String! createdAt: DateTime! updatedAt: DateTime! } ​

接口在查询中的意义是:当一个字段返回接口类型时,客户端需要用内联片段来选择特定实现类型的字段:

{ feed { id ... on Post { title content } ... on Video { url duration thumbnailUrl } commentsCount } } ​

接口让Schema的设计更加结构化——你可以查询所有实现了 Commentable 接口的类型,确保它们都有 comments 和 commentsCount 字段。

联合类型:运行时多态

联合类型允许一个字段返回几种不同类型中的任意一种。它和接口的区别是:接口要求实现类型必须包含接口定义的所有字段,而联合类型只要求返回值是其中一种类型,不要求共享字段。

union Media = Photo | Video | Audio union SearchResult = User | Post | Tag union Notification = LikeNotification | CommentNotification | FollowNotification ​

查询联合类型时,必须使用内联片段或带类型条件的片段:

{ search(query: "react") { __typename # "User" | "Post" | "Tag" ... on User { id name bio } ... on Post { id title author { name } } ... on Tag { id name postCount } } } ​

__typename 字段在联合类型查询中几乎是必需的——客户端用它来判断返回值的具体类型,然后走不同的渲染逻辑。

输入类型:Mutation的参数容器

输入类型专门用于Mutation(和Query)的参数。它的语法与对象类型类似,但字段都是输入字段,不是输出字段。输入类型之间可以嵌套。

input CreatePostInput { title: String! content: String! tags: [String!] = [] status: PostStatus = DRAFT scheduledAt: DateTime } input UpdateProfileInput { name: String bio: String avatarUrl: String website: URL } type Mutation { createPost(input: CreatePostInput!): Post! updateProfile(input: UpdateProfileInput!): User! } ​

输入类型和对象类型的区别:

  • 输入类型的字段用 input 定义,不能引用接口和联合类型
  • 对象类型的字段用 type 定义,可以引用接口和联合类型
  • 输入类型用于输入(参数),对象类型用于输出(返回值)

这个分离虽然看起来繁琐,但解决了参数传递的结构化问题——与其把十几个平铺参数传给Mutation,不如用一个输入类型把它们组织起来。

类型之间的关系总览

这些类型构造组合在一起,构成了一个表达力很强的类型系统。在实际Schema设计中,接口用于定义共享契约(如 Node、Timestamped),联合类型用于多态返回值(如搜索结果),枚举用于约束字段值,输入类型用于结构化Mutation参数,自定义标量用于领域特定的值验证。

图:类型系统的适用场景

图:类型系统的适用场景

理解每种类型的适用场景,是设计好Schema的关键。

作者与出处
原作者:灏天文库
来源:平台策划编纂
整理人: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U