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