Skip to content

Commit 6bcede2

Browse files
committed
New GraphQLSemanticNonNull type
1 parent 9a91e33 commit 6bcede2

File tree

1 file changed

+104
-7
lines changed

1 file changed

+104
-7
lines changed

src/type/definition.ts

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ export type GraphQLType =
6666
| GraphQLEnumType
6767
| GraphQLInputObjectType
6868
| GraphQLList<GraphQLType>
69+
>
70+
| GraphQLSemanticNonNull<
71+
| GraphQLScalarType
72+
| GraphQLObjectType
73+
| GraphQLInterfaceType
74+
| GraphQLUnionType
75+
| GraphQLEnumType
76+
| GraphQLInputObjectType
77+
| GraphQLList<GraphQLType>
6978
>;
7079

7180
export function isType(type: unknown): type is GraphQLType {
@@ -77,7 +86,8 @@ export function isType(type: unknown): type is GraphQLType {
7786
isEnumType(type) ||
7887
isInputObjectType(type) ||
7988
isListType(type) ||
80-
isNonNullType(type)
89+
isNonNullType(type) ||
90+
isSemanticNonNullType(type)
8191
);
8292
}
8393

@@ -203,6 +213,32 @@ export function assertNonNullType(type: unknown): GraphQLNonNull<GraphQLType> {
203213
return type;
204214
}
205215

216+
export function isSemanticNonNullType(
217+
type: GraphQLInputType,
218+
): type is GraphQLSemanticNonNull<GraphQLInputType>;
219+
export function isSemanticNonNullType(
220+
type: GraphQLOutputType,
221+
): type is GraphQLSemanticNonNull<GraphQLOutputType>;
222+
export function isSemanticNonNullType(
223+
type: unknown,
224+
): type is GraphQLSemanticNonNull<GraphQLType>;
225+
export function isSemanticNonNullType(
226+
type: unknown,
227+
): type is GraphQLSemanticNonNull<GraphQLType> {
228+
return instanceOf(type, GraphQLSemanticNonNull);
229+
}
230+
231+
export function assertSemanticNonNullType(
232+
type: unknown,
233+
): GraphQLSemanticNonNull<GraphQLType> {
234+
if (!isSemanticNonNullType(type)) {
235+
throw new Error(
236+
`Expected ${inspect(type)} to be a GraphQL Semantic-Non-Null type.`,
237+
);
238+
}
239+
return type;
240+
}
241+
206242
/**
207243
* These types may be used as input types for arguments and directives.
208244
*/
@@ -217,6 +253,7 @@ export type GraphQLInputType =
217253
| GraphQLInputObjectType
218254
| GraphQLList<GraphQLInputType>
219255
>;
256+
// Note: GraphQLSemanticNonNull is currently not allowed for input types
220257

221258
export function isInputType(type: unknown): type is GraphQLInputType {
222259
return (
@@ -251,6 +288,14 @@ export type GraphQLOutputType =
251288
| GraphQLUnionType
252289
| GraphQLEnumType
253290
| GraphQLList<GraphQLOutputType>
291+
>
292+
| GraphQLSemanticNonNull<
293+
| GraphQLScalarType
294+
| GraphQLObjectType
295+
| GraphQLInterfaceType
296+
| GraphQLUnionType
297+
| GraphQLEnumType
298+
| GraphQLList<GraphQLOutputType>
254299
>;
255300

256301
export function isOutputType(type: unknown): type is GraphQLOutputType {
@@ -414,16 +459,66 @@ export class GraphQLNonNull<T extends GraphQLNullableType> {
414459
}
415460
}
416461

462+
/**
463+
* Semantic-Non-Null Type Wrapper
464+
*
465+
* A semantic-non-null is a wrapping type which points to another type.
466+
* Semantic-non-null types enforce that their values are never null unless
467+
* caused by an error being raised. It is useful for fields which you can make
468+
* a guarantee on non-nullability in a no-error case, for example when you know
469+
* that a related entity must exist (but acknowledge that retrieving it may
470+
* produce an error).
471+
*
472+
* Example:
473+
*
474+
* ```ts
475+
* const RowType = new GraphQLObjectType({
476+
* name: 'Row',
477+
* fields: () => ({
478+
* email: { type: new GraphQLSemanticNonNull(GraphQLString) },
479+
* })
480+
* })
481+
* ```
482+
* Note: the enforcement of non-nullability occurs within the executor.
483+
*
484+
* @experimental
485+
*/
486+
export class GraphQLSemanticNonNull<T extends GraphQLNullableType> {
487+
readonly ofType: T;
488+
489+
constructor(ofType: T) {
490+
devAssert(
491+
isNullableType(ofType),
492+
`Expected ${inspect(ofType)} to be a GraphQL nullable type.`,
493+
);
494+
495+
this.ofType = ofType;
496+
}
497+
498+
get [Symbol.toStringTag]() {
499+
return 'GraphQLSemanticNonNull';
500+
}
501+
502+
toString(): string {
503+
return String(this.ofType) + '*';
504+
}
505+
506+
toJSON(): string {
507+
return this.toString();
508+
}
509+
}
510+
417511
/**
418512
* These types wrap and modify other types
419513
*/
420514

421515
export type GraphQLWrappingType =
422516
| GraphQLList<GraphQLType>
423-
| GraphQLNonNull<GraphQLType>;
517+
| GraphQLNonNull<GraphQLType>
518+
| GraphQLSemanticNonNull<GraphQLType>;
424519

425520
export function isWrappingType(type: unknown): type is GraphQLWrappingType {
426-
return isListType(type) || isNonNullType(type);
521+
return isListType(type) || isNonNullType(type) || isSemanticNonNullType(type);
427522
}
428523

429524
export function assertWrappingType(type: unknown): GraphQLWrappingType {
@@ -446,7 +541,7 @@ export type GraphQLNullableType =
446541
| GraphQLList<GraphQLType>;
447542

448543
export function isNullableType(type: unknown): type is GraphQLNullableType {
449-
return isType(type) && !isNonNullType(type);
544+
return isType(type) && !isNonNullType(type) && !isSemanticNonNullType(type);
450545
}
451546

452547
export function assertNullableType(type: unknown): GraphQLNullableType {
@@ -458,7 +553,7 @@ export function assertNullableType(type: unknown): GraphQLNullableType {
458553

459554
export function getNullableType(type: undefined | null): void;
460555
export function getNullableType<T extends GraphQLNullableType>(
461-
type: T | GraphQLNonNull<T>,
556+
type: T | GraphQLNonNull<T> | GraphQLSemanticNonNull<T>,
462557
): T;
463558
export function getNullableType(
464559
type: Maybe<GraphQLType>,
@@ -467,12 +562,14 @@ export function getNullableType(
467562
type: Maybe<GraphQLType>,
468563
): GraphQLNullableType | undefined {
469564
if (type) {
470-
return isNonNullType(type) ? type.ofType : type;
565+
return isNonNullType(type) || isSemanticNonNullType(type)
566+
? type.ofType
567+
: type;
471568
}
472569
}
473570

474571
/**
475-
* These named types do not include modifiers like List or NonNull.
572+
* These named types do not include modifiers like List, NonNull, or SemanticNonNull
476573
*/
477574
export type GraphQLNamedType = GraphQLNamedInputType | GraphQLNamedOutputType;
478575

0 commit comments

Comments
 (0)