联合类型与接口(Interface)非常相似,但它们之间没有任何共同字段(详细说明可参考这里)。联合类型适用于需要从单个字段返回多种不相交数据类型的场景。
要定义一个 GraphQL 联合类型,我们需要先定义该联合类型所包含的各个类。参考 Apollo 官方文档中的示例,我们将创建两个类。首先是 Book:
import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType()
export class Book {
@Field()
title: string
}接下来是 Author:
import { Field, ObjectType } from '@nestjs/graphql'
@ObjectType()
export class Author {
@Field()
name: string
}完成上述定义后,我们可以通过 @nestjs/graphql 包中导出的 createUnionType 函数注册 ResultUnion 联合类型:
export const ResultUnion = createUnionType({
name: 'ResultUnion',
types: () => [Author, Book] as const,
})通过 createUnionType 函数的 types 属性返回的数组应加上 const
断言。如果没有加 const
断言,编译时会生成错误的声明文件,并且在其他项目中使用时会报错。
现在,我们可以在查询中引用 ResultUnion:
@Query(() => [ResultUnion])
search(): Array<typeof ResultUnion> {
return [new Author(), new Book()]
}这样会在生成的 GraphQL SDL 中得到如下片段:
type Author {
name: String!
}
type Book {
title: String!
}
union ResultUnion = Author | Book
type Query {
search: [ResultUnion!]!
}库默认生成的 resolveType() 函数会根据解析器方法返回值自动提取类型。这意味着必须返回类实例,而不能返回普通的 JavaScript 字面量对象。
如果需要自定义 resolveType() 函数,可以在传递给 createUnionType() 的选项对象中添加 resolveType 属性,如下所示:
export const ResultUnion = createUnionType({
name: 'ResultUnion',
types: () => [Author, Book] as const,
resolveType(value) {
if (value.name) {
return Author
}
if (value.title) {
return Book
}
return null
},
})在模式优先方式中,定义联合类型非常简单,只需使用 SDL 创建一个 GraphQL 联合类型即可。
type Author {
name: String!
}
type Book {
title: String!
}
union ResultUnion = Author | Book接下来,你可以使用类型定义自动生成功能(详见快速入门章节),自动生成对应的 TypeScript 类型定义:
export class Author {
name: string
}
export class Book {
title: string
}
export type ResultUnion = Author | Book在 GraphQL 中,联合类型解析时需要在解析器映射(resolver map)中额外实现一个 __resolveType 字段,用于确定当前联合类型实际应解析为哪种类型。同时,需要注意,ResultUnionResolver 类必须作为提供者注册到某个模块中。下面我们来创建一个 ResultUnionResolver 类,并定义 __resolveType 方法。
@Resolver('ResultUnion')
export class ResultUnionResolver {
@ResolveField()
__resolveType(value) {
if (value.name) {
return 'Author'
}
if (value.title) {
return 'Book'
}
return null
}
}@nestjs/graphql 包中导出。枚举类型是一种特殊的标量类型,其值被限定为一组特定的可选值(详细内容可参考这里)。这样做可以:
在代码优先的方式下,你只需创建一个 TypeScript 枚举,即可定义一个 GraphQL 枚举类型。
export enum AllowedColor {
RED,
GREEN,
BLUE,
}完成上述定义后,使用 @nestjs/graphql 包中导出的 registerEnumType 函数注册 AllowedColor 枚举:
registerEnumType(AllowedColor, {
name: 'AllowedColor',
})现在,你可以在类型定义中引用 AllowedColor:
@Field(type => AllowedColor)
favoriteColor: AllowedColor这样会在生成的 GraphQL 架构 SDL 中得到如下内容:
enum AllowedColor {
RED
GREEN
BLUE
}如需为枚举类型添加描述,可以在 registerEnumType() 函数中传入 description 属性:
registerEnumType(AllowedColor, {
name: 'AllowedColor',
description: '支持的颜色。',
})如需为枚举值添加描述,或标记某个值为已弃用,可以传入 valuesMap 属性,如下所示:
registerEnumType(AllowedColor, {
name: 'AllowedColor',
description: '支持的颜色。',
valuesMap: {
RED: {
description: '默认颜色。',
},
BLUE: {
deprecationReason: '颜色过于蓝。',
},
},
})这将在生成的 GraphQL 架构 SDL 中得到如下内容:
"""
支持的颜色。
"""
enum AllowedColor {
"""
默认颜色。
"""
RED
GREEN
BLUE @deprecated(reason: "颜色过于蓝。")
}在架构优先方式下,只需在 SDL 中定义 GraphQL 枚举类型:
enum AllowedColor {
RED
GREEN
BLUE
}然后,你可以使用类型定义自动生成工具(具体用法见快速入门章节)生成对应的 TypeScript 定义:
export enum AllowedColor {
RED
GREEN
BLUE
}有时,后端内部枚举值与 API 公共接口中的值不同。例如,API 中为 RED,但在解析器中实际使用 #f00(详细说明见这里)。要实现这一点,可以为 AllowedColor 枚举声明一个解析器对象:
export const allowedColorResolver: Record<keyof typeof AllowedColor, any> = {
RED: '#f00',
}所有装饰器均从 @nestjs/graphql 包中导出。
然后,将该解析器对象与 GraphQLModule#forRoot() 方法的 resolvers 属性一起使用,如下所示:
GraphQLModule.forRoot({
resolvers: {
AllowedColor: allowedColorResolver,
},
})