如何在NestJS GraphQL中为输入字段定义多种类型
NestJS GraphQL 实现多类型输入字段的解决方案
问题场景
需要为KeyValuePair输入类型的value字段定义三种可选类型:string[]、RangeInput、number[],但直接使用联合类型写法无法生效,尝试过@Field(() => [String, RangeInput, Number])、@Field(() => [Object])、@Field(() => [JSON])均未达到预期效果。
现有代码示例:
@InputType() class RangeInput implements RangeInterface { @Field(type => String) start: string; @Field(type => String) end: string; } @InputType() class KeyValuePair implements KeyValuePairInterface { @Field() key: string; @Field(() => [String, RangeInput, Number]) value: string[] | RangeInput | number[]; @Field(() => Boolean) matchExact: boolean; @Field(() => FilterType) filterType: FilterType; }
核心原因
GraphQL 规范不支持输入类型的联合(Union),仅输出类型允许使用联合类型,因此无法直接为输入字段定义string[] | RangeInput | number[]这种联合类型。
可行解决方案
方案1:带判别字段的输入类型(兼容性最佳)
通过添加一个类型判别字段,配合条件验证,确保每次仅传入一种类型的有效值:
import { InputType, Field, registerEnumType } from '@nestjs/graphql'; import { ValidateIf } from 'class-validator'; // 定义类型枚举,用于标记当前value的类型 export enum ValueType { STRING_ARRAY = 'STRING_ARRAY', RANGE = 'RANGE', NUMBER_ARRAY = 'NUMBER_ARRAY', } registerEnumType(ValueType, { name: 'ValueType' }); @InputType() class RangeInput implements RangeInterface { @Field(type => String) start: string; @Field(type => String) end: string; } @InputType() class KeyValuePair implements KeyValuePairInterface { @Field() key: string; // 类型判别字段,必填 @Field(() => ValueType) valueType: ValueType; // 对应string[]类型,仅当valueType为STRING_ARRAY时生效 @Field(() => [String], { nullable: true }) @ValidateIf(o => o.valueType === ValueType.STRING_ARRAY) stringValue?: string[]; // 对应RangeInput类型,仅当valueType为RANGE时生效 @Field(() => RangeInput, { nullable: true }) @ValidateIf(o => o.valueType === ValueType.RANGE) rangeValue?: RangeInput; // 对应number[]类型,仅当valueType为NUMBER_ARRAY时生效 @Field(() => [Number], { nullable: true }) @ValidateIf(o => o.valueType === ValueType.NUMBER_ARRAY) numberValue?: number[]; @Field(() => Boolean) matchExact: boolean; @Field(() => FilterType) filterType: FilterType; }
使用时,客户端需根据valueType传入对应字段(例如valueType: "STRING_ARRAY"时,传入stringValue: ["foo", "bar"]),后端通过valueType判断读取对应字段的值即可。
方案2:使用OneOf装饰器(需GraphQL版本支持)
若使用的GraphQL版本(16.6+)支持OneOf特性,可通过NestJS的@OneOf装饰器实现类似联合输入的效果:
import { InputType, Field, OneOf } from '@nestjs/graphql'; @InputType() class RangeInput implements RangeInterface { @Field(type => String) start: string; @Field(type => String) end: string; } @InputType() class KeyValuePair implements KeyValuePairInterface { @Field() key: string; @OneOf(() => [[String], RangeInput, [Number]]) @Field(() => [[String], RangeInput, [Number]], { nullable: false }) value: string[] | RangeInput | number[]; @Field(() => Boolean) matchExact: boolean; @Field(() => FilterType) filterType: FilterType; }
注意:该方案需要服务端和客户端均支持GraphQL的OneOf特性,兼容性相对较弱。
内容的提问来源于stack exchange,提问作者Jamsheed
相关产品推荐
相关产品推荐

