GraphQL 类型系统 #

类型系统概述 #

GraphQL 是强类型语言,每个字段都有明确的类型定义。类型系统是 GraphQL 的核心特性之一。

text
┌─────────────────────────────────────────────────────────────┐
│                    GraphQL 类型系统                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  类型分类:                                                  │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  标量类型    │  │  对象类型    │  │  枚举类型    │         │
│  │  Scalar     │  │  Object     │  │  Enum       │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  接口类型    │  │  联合类型    │  │  输入类型    │         │
│  │  Interface  │  │  Union      │  │  Input      │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│  ┌─────────────┐  ┌─────────────┐                          │
│  │  列表类型    │  │  非空类型    │                          │
│  │  List       │  │  Non-Null   │                          │
│  └─────────────┘  └─────────────┘                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

标量类型(Scalar) #

标量类型是最基本的数据类型,表示单个值。

内置标量 #

graphql
type User {
  id: ID!
  name: String!
  age: Int
  score: Float
  isActive: Boolean!
}
类型 说明 示例
Int 有符号 32 位整数 42
Float 有符号双精度浮点数 3.14
String UTF-8 字符串 "Hello"
Boolean 布尔值 true / false
ID 唯一标识符 "1"1

自定义标量 #

graphql
scalar DateTime
scalar Email
scalar URL
scalar JSON
scalar PositiveInt
scalar PhoneNumber

标量实现 #

javascript
const { GraphQLScalarType, Kind } = require('graphql');

const DateTime = new GraphQLScalarType({
  name: 'DateTime',
  description: 'ISO 8601 格式的日期时间',
  
  parseValue(value) {
    return new Date(value);
  },
  
  serialize(value) {
    return value.toISOString();
  },
  
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      return new Date(ast.value);
    }
    return null;
  }
});

const Email = new GraphQLScalarType({
  name: 'Email',
  description: '邮箱地址',
  
  parseValue(value) {
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      throw new Error('Invalid email format');
    }
    return value;
  },
  
  serialize(value) {
    return value;
  },
  
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(ast.value)) {
        throw new Error('Invalid email format');
      }
      return ast.value;
    }
    return null;
  }
});

const PositiveInt = new GraphQLScalarType({
  name: 'PositiveInt',
  description: '正整数',
  
  parseValue(value) {
    if (typeof value !== 'number' || value <= 0 || !Number.isInteger(value)) {
      throw new Error('Value must be a positive integer');
    }
    return value;
  },
  
  serialize(value) {
    return value;
  },
  
  parseLiteral(ast) {
    if (ast.kind === Kind.INT) {
      const value = parseInt(ast.value, 10);
      if (value > 0) {
        return value;
      }
    }
    return null;
  }
});

使用自定义标量 #

graphql
type User {
  id: ID!
  name: String!
  email: Email!
  avatar: URL
  birthDate: DateTime
  metadata: JSON
  age: PositiveInt
}

对象类型(Object) #

对象类型表示一组字段的集合。

基本定义 #

graphql
type User {
  id: ID!
  name: String!
  email: String!
  age: Int
  status: UserStatus!
  createdAt: DateTime!
}

字段参数 #

graphql
type User {
  id: ID!
  name: String!
  posts(limit: Int = 10, status: PostStatus): [Post!]!
  comments(first: Int = 5): [Comment!]!
}

实现接口 #

graphql
interface Node {
  id: ID!
  createdAt: DateTime!
}

interface Timestamped {
  createdAt: DateTime!
  updatedAt: DateTime!
}

type User implements Node & Timestamped {
  id: ID!
  name: String!
  email: String!
  createdAt: DateTime!
  updatedAt: DateTime!
}

字段描述 #

graphql
type User {
  id: ID!
  name: String!
  email: String!
  age: Int
  status: UserStatus!
  createdAt: DateTime!
}

枚举类型(Enum) #

枚举类型定义一组有限的值。

基本定义 #

graphql
enum UserStatus {
  ACTIVE
  INACTIVE
  PENDING
  SUSPENDED
  DELETED
}

enum Role {
  ADMIN
  MODERATOR
  USER
  GUEST
}

enum PostStatus {
  DRAFT
  PUBLISHED
  ARCHIVED
  DELETED
}

使用枚举 #

graphql
type User {
  id: ID!
  name: String!
  status: UserStatus!
  role: Role!
}

type Query {
  users(status: UserStatus, role: Role): [User!]!
}

type Mutation {
  updateUserStatus(id: ID!, status: UserStatus!): User!
}

枚举值描述 #

graphql
enum UserStatus {
  ACTIVE
  INACTIVE
  PENDING
  SUSPENDED
  DELETED
}

枚举实现 #

javascript
const { GraphQLEnumType } = require('graphql');

const UserStatus = new GraphQLEnumType({
  name: 'UserStatus',
  description: '用户状态',
  values: {
    ACTIVE: {
      value: 'active',
      description: '活跃用户'
    },
    INACTIVE: {
      value: 'inactive',
      description: '非活跃用户'
    },
    PENDING: {
      value: 'pending',
      description: '待激活用户'
    },
    SUSPENDED: {
      value: 'suspended',
      description: '已暂停用户'
    },
    DELETED: {
      value: 'deleted',
      description: '已删除用户'
    }
  }
});

接口类型(Interface) #

接口定义一组公共字段,用于抽象和复用。

定义接口 #

graphql
interface Node {
  id: ID!
}

interface Timestamped {
  createdAt: DateTime!
  updatedAt: DateTime
}

interface Content {
  id: ID!
  title: String!
  author: User!
  createdAt: DateTime!
}

实现接口 #

graphql
type Post implements Node & Content & Timestamped {
  id: ID!
  title: String!
  content: String!
  author: User!
  tags: [String!]!
  createdAt: DateTime!
  updatedAt: DateTime
}

type Page implements Node & Content & Timestamped {
  id: ID!
  title: String!
  body: String!
  author: User!
  slug: String!
  createdAt: DateTime!
  updatedAt: DateTime
}

type Comment implements Node & Timestamped {
  id: ID!
  content: String!
  author: User!
  post: Post!
  createdAt: DateTime!
  updatedAt: DateTime
}

查询接口 #

graphql
type Query {
  node(id: ID!): Node
  content(id: ID!): Content
  nodes(ids: [ID!]!): [Node]!
}

使用内联片段 #

graphql
query {
  content(id: "1") {
    id
    title
    author {
      name
    }
    ... on Post {
      tags
      content
    }
    ... on Page {
      slug
      body
    }
  }
}

接口解析器 #

javascript
const resolvers = {
  Node: {
    __resolveType(obj) {
      if (obj.postId) return 'Post';
      if (obj.pageId) return 'Page';
      if (obj.commentId) return 'Comment';
      return null;
    }
  },
  Content: {
    __resolveType(obj) {
      if (obj.content) return 'Post';
      if (obj.body) return 'Page';
      return null;
    }
  }
};

联合类型(Union) #

联合类型表示多种类型之一,不需要共享字段。

定义联合类型 #

graphql
union SearchResult = User | Post | Comment

union Media = Image | Video | Audio

union Notification = UserNotification | SystemNotification | MessageNotification

使用联合类型 #

graphql
type Query {
  search(query: String!): [SearchResult!]!
  media(id: ID!): Media
  notifications: [Notification!]!
}

查询联合类型 #

graphql
query {
  search(query: "GraphQL") {
    ... on User {
      id
      name
      email
    }
    ... on Post {
      id
      title
      excerpt
    }
    ... on Comment {
      id
      content
    }
  }
}

联合类型解析器 #

javascript
const resolvers = {
  SearchResult: {
    __resolveType(obj) {
      if (obj.email) return 'User';
      if (obj.title) return 'Post';
      if (obj.content && obj.postId) return 'Comment';
      return null;
    }
  },
  Media: {
    __resolveType(obj) {
      if (obj.width) return 'Image';
      if (obj.duration && obj.thumbnail) return 'Video';
      if (obj.duration && !obj.thumbnail) return 'Audio';
      return null;
    }
  }
};

接口 vs 联合类型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    接口 vs 联合类型                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  接口(Interface):                                        │
│  ✅ 共享字段                                                 │
│  ✅ 行为契约                                                │
│  ✅ 类型继承                                                │
│  示例:Node、Content、Timestamped                           │
│                                                             │
│  联合类型(Union):                                        │
│  ✅ 不需要共享字段                                           │
│  ✅ 完全不同的类型                                           │
│  ✅ 搜索结果、通知等                                         │
│  示例:SearchResult、Media、Notification                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

输入类型(Input) #

输入类型用于传递复杂参数。

基本定义 #

graphql
input CreateUserInput {
  name: String!
  email: String!
  password: String!
  age: Int
  role: Role = USER
}

input UpdateUserInput {
  name: String
  email: String
  age: Int
  status: UserStatus
}

嵌套输入 #

graphql
input CreatePostInput {
  title: String!
  content: String!
  excerpt: String
  status: PostStatus = DRAFT
  tags: [String!]
  category: CategoryInput
  seo: SEOInput
}

input CategoryInput {
  id: ID
  name: String
  slug: String
}

input SEOInput {
  title: String
  description: String
  keywords: [String!]
}

过滤输入 #

graphql
input UserFilter {
  status: UserStatus
  role: Role
  age: IntRange
  name: StringFilter
  createdAt: DateRange
  AND: [UserFilter!]
  OR: [UserFilter!]
  NOT: UserFilter
}

input IntRange {
  gte: Int
  gt: Int
  lte: Int
  lt: Int
  eq: Int
  ne: Int
  in: [Int!]
  nin: [Int!]
}

input StringFilter {
  eq: String
  ne: String
  contains: String
  startsWith: String
  endsWith: String
  in: [String!]
  nin: [String!]
  regex: String
}

input DateRange {
  gte: DateTime
  gt: DateTime
  lte: DateTime
  lt: DateTime
}

排序输入 #

graphql
input UserOrderBy {
  field: UserOrderField!
  direction: SortOrder!
}

enum UserOrderField {
  NAME
  EMAIL
  AGE
  CREATED_AT
  UPDATED_AT
}

enum SortOrder {
  ASC
  DESC
}

使用输入类型 #

graphql
type Query {
  users(
    filter: UserFilter
    orderBy: UserOrderBy
    first: Int
    after: String
  ): UserConnection!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
}

输入类型 vs 对象类型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    输入类型 vs 对象类型                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  输入类型(Input):                                        │
│  - 用于参数传递                                             │
│  - 不能有参数                                               │
│  - 不能实现接口                                             │
│  - 不能包含对象类型                                         │
│                                                             │
│  对象类型(Object):                                       │
│  - 用于返回数据                                             │
│  - 字段可以有参数                                           │
│  - 可以实现接口                                             │
│  - 可以包含其他对象类型                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

列表类型(List) #

列表类型表示值的数组。

基本用法 #

graphql
type User {
  id: ID!
  name: String!
  tags: [String!]!
  posts: [Post!]!
  emails: [Email!]!
}

列表修饰符 #

graphql
type Example {
  requiredList: [String]!
  nullableList: [String]
  requiredItems: [String!]!
  mixed: [String!]
}
类型 列表可空 元素可空 示例
[String]! ["a", null, "b"]
[String] null["a", null]
[String!]! ["a", "b"]
[String!] null["a", "b"]

嵌套列表 #

graphql
type Matrix {
  data: [[Int!]!]!
  coordinates: [[Float!]!]!
}

非空类型(Non-Null) #

非空类型表示值不能为 null。

基本用法 #

graphql
type User {
  id: ID!
  name: String!
  email: String!
  bio: String
  avatar: String
}

非空修饰符 #

graphql
type Example {
  required: String!
  optional: String
  requiredList: [String]!
  requiredItems: [String!]!
  allRequired: [String!]!
}

非空与错误 #

graphql
query {
  user(id: "non-existent") {
    id
    name!
  }
}

如果 name 为 null,整个 user 字段会返回 null。

最佳实践 #

text
┌─────────────────────────────────────────────────────────────┐
│                    非空类型使用建议                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  应该使用非空:                                              │
│  ✅ ID 字段                                                 │
│  ✅ 必填字段(如用户名、邮箱)                               │
│  ✅ 列表类型(如果列表本身不应为 null)                      │
│  ✅ 枚举字段(如果总有值)                                   │
│                                                             │
│  应该可空:                                                  │
│  ✅ 可选字段(如头像、简介)                                 │
│  ✅ 可能不存在的关联数据                                     │
│  ✅ 可能为 null 的计算字段                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

类型修饰符组合 #

graphql
type Example {
  a: String
  b: String!
  c: [String]
  d: [String]!
  e: [String!]
  f: [String!]!
  g: [[String!]!]!
}
字段 类型 可空性
a String 字段可空,值可空
b String! 字段不可空,值不可空
c [String] 列表可空,元素可空
d [String]! 列表不可空,元素可空
e [String!] 列表可空,元素不可空
f [String!]! 列表不可空,元素不可空
g [[String!]!]! 嵌套列表,全部不可空

类型系统最佳实践 #

1. 合理使用非空 #

graphql
type User {
  id: ID!
  name: String!
  email: String!
  avatar: String
  bio: String
  phone: String
}

2. 使用枚举代替字符串 #

graphql
enum UserStatus {
  ACTIVE
  INACTIVE
  PENDING
}

type User {
  status: UserStatus!
}

3. 使用接口抽象公共字段 #

graphql
interface Node {
  id: ID!
}

interface Timestamped {
  createdAt: DateTime!
  updatedAt: DateTime
}

type User implements Node & Timestamped {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime
}

4. 分离输入类型 #

graphql
input CreateUserInput {
  name: String!
  email: String!
  password: String!
}

input UpdateUserInput {
  name: String
  email: String
}

5. 使用自定义标量 #

graphql
type User {
  email: Email!
  avatar: URL
  birthDate: DateTime
}

下一步 #

现在你已经掌握了 GraphQL 类型系统,接下来学习 解析器,了解如何实现数据获取逻辑!

最后更新:2026-03-29