@@ -19,6 +19,7 @@ import {
1919 GraphQLEnumType ,
2020 GraphQLList ,
2121 GraphQLNonNull ,
22+ GraphQLSemanticNonNull ,
2223 GraphQLObjectType ,
2324 isAbstractType ,
2425 isEnumType ,
@@ -205,6 +206,40 @@ export const __DirectiveLocation: GraphQLEnumType = new GraphQLEnumType({
205206 } ,
206207} ) ;
207208
209+ // TODO: rename enum and options
210+ enum TypeNullability {
211+ AUTO = 'AUTO' ,
212+ TRADITIONAL = 'TRADITIONAL' ,
213+ SEMANTIC = 'SEMANTIC' ,
214+ FULL = 'FULL' ,
215+ }
216+
217+ // TODO: rename
218+ export const __TypeNullability : GraphQLEnumType = new GraphQLEnumType ( {
219+ name : '__TypeNullability' ,
220+ description : 'TODO' ,
221+ values : {
222+ AUTO : {
223+ value : TypeNullability . AUTO ,
224+ description :
225+ 'Determines nullability mode based on errorPropagation mode.' ,
226+ } ,
227+ TRADITIONAL : {
228+ value : TypeNullability . TRADITIONAL ,
229+ description : 'Turn semantic-non-null types into nullable types.' ,
230+ } ,
231+ SEMANTIC : {
232+ value : TypeNullability . SEMANTIC ,
233+ description : 'Turn non-null types into semantic-non-null types.' ,
234+ } ,
235+ FULL : {
236+ value : TypeNullability . FULL ,
237+ description :
238+ 'Render the true nullability in the schema; be prepared for new types of nullability in future!' ,
239+ } ,
240+ } ,
241+ } ) ;
242+
208243export const __Type : GraphQLObjectType = new GraphQLObjectType ( {
209244 name : '__Type' ,
210245 description :
@@ -370,7 +405,25 @@ export const __Field: GraphQLObjectType = new GraphQLObjectType({
370405 } ,
371406 type : {
372407 type : new GraphQLNonNull ( __Type ) ,
373- resolve : ( field ) => field . type ,
408+ args : {
409+ nullability : {
410+ type : __TypeNullability ,
411+ defaultValue : 'AUTO' ,
412+ } ,
413+ } ,
414+ resolve : ( field , { nullability } , _context , info ) => {
415+ if ( nullability === TypeNullability . FULL ) {
416+ return field . type ;
417+ } else {
418+ const mode =
419+ nullability === TypeNullability . AUTO
420+ ? info . errorPropagation
421+ ? TypeNullability . TRADITIONAL
422+ : TypeNullability . SEMANTIC
423+ : nullability ;
424+ return convertOutputTypeToNullabilityMode ( field . type , mode ) ;
425+ }
426+ } ,
374427 } ,
375428 isDeprecated : {
376429 type : new GraphQLNonNull ( GraphQLBoolean ) ,
@@ -383,6 +436,40 @@ export const __Field: GraphQLObjectType = new GraphQLObjectType({
383436 } as GraphQLFieldConfigMap < GraphQLField < unknown , unknown > , unknown > ) ,
384437} ) ;
385438
439+ // TODO: move this elsewhere, rename, memoize
440+ function convertOutputTypeToNullabilityMode (
441+ type : GraphQLType ,
442+ mode : TypeNullability . TRADITIONAL | TypeNullability . SEMANTIC ,
443+ ) : GraphQLType {
444+ if ( mode === TypeNullability . TRADITIONAL ) {
445+ if ( isNonNullType ( type ) ) {
446+ return new GraphQLNonNull (
447+ convertOutputTypeToNullabilityMode ( type . ofType , mode ) ,
448+ ) ;
449+ } else if ( isSemanticNonNullType ( type ) ) {
450+ return convertOutputTypeToNullabilityMode ( type . ofType , mode ) ;
451+ } else if ( isListType ( type ) ) {
452+ return new GraphQLList (
453+ convertOutputTypeToNullabilityMode ( type . ofType , mode ) ,
454+ ) ;
455+ } else {
456+ return type ;
457+ }
458+ } else {
459+ if ( isNonNullType ( type ) || isSemanticNonNullType ( type ) ) {
460+ return new GraphQLSemanticNonNull (
461+ convertOutputTypeToNullabilityMode ( type . ofType , mode ) ,
462+ ) ;
463+ } else if ( isListType ( type ) ) {
464+ return new GraphQLList (
465+ convertOutputTypeToNullabilityMode ( type . ofType , mode ) ,
466+ ) ;
467+ } else {
468+ return type ;
469+ }
470+ }
471+ }
472+
386473export const __InputValue : GraphQLObjectType = new GraphQLObjectType ( {
387474 name : '__InputValue' ,
388475 description :
0 commit comments