From cafef7bdcf200fde1e19e65191fb9b1e865a907a Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Mon, 10 May 2021 11:44:12 -0700 Subject: [PATCH] Preserve defaultValue literals Fixes #3051 This change solves the problem of default values defined via SDL not always resolving correctly through introspection by preserving the original GraphQL literal in the schema definition. This changes argument and input field definitions `defaultValue` field from just the "value" to a new `GraphQLDefaultValueUsage` type which contains either or both "value" and "literal" fields. Since either of these fields may be set, new functions for resolving a value or literal from either have been added - `getLiteralDefaultValue` and `getCoercedDefaultValue` - these replace uses that either assumed a set value or were manually converting a value back to a literal. Here is the flow for how a default value defined in an SDL would be converted into a functional schema and back to an SDL: **Before this change:** ``` (SDL) --parse-> (AST) --coerceInputLiteral--> (defaultValue config) --valueToAST--> (AST) --print --> (SDL) ``` `coerceInputLiteral` performs coercion which is a one-way function, and `valueToAST` is unsafe and set to be deprecated in #3049. **After this change:** ``` (SDL) --parse-> (defaultValue literal config) --print --> (SDL) ``` --- src/execution/values.ts | 15 +++-- src/index.ts | 1 + src/type/__tests__/definition-test.ts | 60 +++++++++++++++++++ src/type/definition.ts | 37 ++++++++++-- src/type/index.ts | 1 + src/type/introspection.ts | 9 ++- src/utilities/TypeInfo.ts | 5 +- .../__tests__/buildClientSchema-test.ts | 23 +++++++ .../__tests__/coerceInputValue-test.ts | 36 ++++++++++- src/utilities/astFromValue.ts | 6 +- src/utilities/buildClientSchema.ts | 13 ++-- src/utilities/coerceInputValue.ts | 39 ++++++++++-- src/utilities/extendSchema.ts | 10 +--- src/utilities/findBreakingChanges.ts | 8 ++- src/utilities/printSchema.ts | 9 ++- src/validation/ValidationContext.ts | 3 +- .../rules/VariablesInAllowedPositionRule.ts | 7 ++- 17 files changed, 233 insertions(+), 49 deletions(-) diff --git a/src/execution/values.ts b/src/execution/values.ts index db67b2f2b37..420be7ad877 100644 --- a/src/execution/values.ts +++ b/src/execution/values.ts @@ -20,6 +20,7 @@ import type { GraphQLDirective } from '../type/directives.js'; import type { GraphQLSchema } from '../type/schema.js'; import { + coerceDefaultValue, coerceInputLiteral, coerceInputValue, } from '../utilities/coerceInputValue.js'; @@ -170,8 +171,11 @@ export function getArgumentValues( const argumentNode = argNodeMap[name]; if (!argumentNode) { - if (argDef.defaultValue !== undefined) { - coercedValues[name] = argDef.defaultValue; + if (argDef.defaultValue) { + coercedValues[name] = coerceDefaultValue( + argDef.defaultValue, + argDef.type, + ); } else if (isNonNullType(argType)) { throw new GraphQLError( `Argument ${argDef} of required type ${argType} was not provided.`, @@ -190,8 +194,11 @@ export function getArgumentValues( variableValues == null || !hasOwnProperty(variableValues, variableName) ) { - if (argDef.defaultValue !== undefined) { - coercedValues[name] = argDef.defaultValue; + if (argDef.defaultValue) { + coercedValues[name] = coerceDefaultValue( + argDef.defaultValue, + argDef.type, + ); } else if (isNonNullType(argType)) { throw new GraphQLError( `Argument ${argDef} of required type ${argType} ` + diff --git a/src/index.ts b/src/index.ts index ed051de17b4..89c8eeac66c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -199,6 +199,7 @@ export type { GraphQLScalarSerializer, GraphQLScalarValueParser, GraphQLScalarLiteralParser, + GraphQLDefaultValueUsage, } from './type/index.js'; // Parse and operate on GraphQL language source files. diff --git a/src/type/__tests__/definition-test.ts b/src/type/__tests__/definition-test.ts index bf7f6d4587d..f523ebaa441 100644 --- a/src/type/__tests__/definition-test.ts +++ b/src/type/__tests__/definition-test.ts @@ -4,6 +4,7 @@ import { describe, it } from 'mocha'; import { identityFunc } from '../../jsutils/identityFunc.js'; import { inspect } from '../../jsutils/inspect.js'; +import { Kind } from '../../language/kinds.js'; import { parseValue } from '../../language/parser.js'; import type { GraphQLNullableType, GraphQLType } from '../definition.js'; @@ -617,6 +618,65 @@ describe('Type System: Input Objects', () => { 'not used anymore', ); }); + + describe('Input Object fields may have default values', () => { + it('accepts an Input Object type with a default value', () => { + const inputObjType = new GraphQLInputObjectType({ + name: 'SomeInputObject', + fields: { + f: { type: ScalarType, defaultValue: 3 }, + }, + }); + expect(inputObjType.getFields().f).to.deep.include({ + coordinate: 'SomeInputObject.f', + name: 'f', + description: undefined, + type: ScalarType, + defaultValue: { value: 3 }, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + }); + + it('accepts an Input Object type with a default value literal', () => { + const inputObjType = new GraphQLInputObjectType({ + name: 'SomeInputObject', + fields: { + f: { + type: ScalarType, + defaultValueLiteral: { kind: Kind.INT, value: '3' }, + }, + }, + }); + expect(inputObjType.getFields().f).to.deep.include({ + coordinate: 'SomeInputObject.f', + name: 'f', + description: undefined, + type: ScalarType, + defaultValue: { literal: { kind: 'IntValue', value: '3' } }, + deprecationReason: undefined, + extensions: {}, + astNode: undefined, + }); + }); + + it('rejects an Input Object type with potentially conflicting default values', () => { + const inputObjType = new GraphQLInputObjectType({ + name: 'SomeInputObject', + fields: { + f: { + type: ScalarType, + defaultValue: 3, + defaultValueLiteral: { kind: Kind.INT, value: '3' }, + }, + }, + }); + expect(() => inputObjType.getFields()).to.throw( + 'f has both a defaultValue and a defaultValueLiteral property, but only one must be provided.', + ); + }); + }); }); describe('Type System: List', () => { diff --git a/src/type/definition.ts b/src/type/definition.ts index 7455dca97df..d202cb9fa4c 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -16,6 +16,7 @@ import { toObjMap } from '../jsutils/toObjMap.js'; import { GraphQLError } from '../error/GraphQLError.js'; import type { + ConstValueNode, EnumTypeDefinitionNode, EnumTypeExtensionNode, EnumValueDefinitionNode, @@ -908,6 +909,7 @@ export interface GraphQLArgumentConfig { description?: Maybe; type: GraphQLInputType; defaultValue?: unknown; + defaultValueLiteral?: ConstValueNode | undefined; deprecationReason?: Maybe; extensions?: Maybe>; astNode?: Maybe; @@ -984,7 +986,7 @@ export class GraphQLArgument extends GraphQLSchemaElement { name: string; description: Maybe; type: GraphQLInputType; - defaultValue: unknown; + defaultValue: GraphQLDefaultValueUsage | undefined; deprecationReason: Maybe; extensions: Readonly; astNode: Maybe; @@ -999,7 +1001,7 @@ export class GraphQLArgument extends GraphQLSchemaElement { this.name = assertName(name); this.description = config.description; this.type = config.type; - this.defaultValue = config.defaultValue; + this.defaultValue = defineDefaultValue(coordinate, config); this.deprecationReason = config.deprecationReason; this.extensions = toObjMap(config.extensions); this.astNode = config.astNode; @@ -1013,7 +1015,8 @@ export class GraphQLArgument extends GraphQLSchemaElement { return { description: this.description, type: this.type, - defaultValue: this.defaultValue, + defaultValue: this.defaultValue?.value, + defaultValueLiteral: this.defaultValue?.literal, deprecationReason: this.deprecationReason, extensions: this.extensions, astNode: this.astNode, @@ -1029,6 +1032,26 @@ export type GraphQLFieldMap = ObjMap< GraphQLField >; +export type GraphQLDefaultValueUsage = + | { value: unknown; literal?: never } + | { literal: ConstValueNode; value?: never }; + +function defineDefaultValue( + coordinate: string, + config: GraphQLArgumentConfig | GraphQLInputFieldConfig, +): GraphQLDefaultValueUsage | undefined { + if (config.defaultValue === undefined && !config.defaultValueLiteral) { + return; + } + devAssert( + !(config.defaultValue !== undefined && config.defaultValueLiteral), + `${coordinate} has both a defaultValue and a defaultValueLiteral property, but only one must be provided.`, + ); + return config.defaultValueLiteral + ? { literal: config.defaultValueLiteral } + : { value: config.defaultValue }; +} + /** * Custom extensions * @@ -1608,6 +1631,7 @@ export interface GraphQLInputFieldConfig { description?: Maybe; type: GraphQLInputType; defaultValue?: unknown; + defaultValueLiteral?: ConstValueNode | undefined; deprecationReason?: Maybe; extensions?: Maybe>; astNode?: Maybe; @@ -1619,7 +1643,7 @@ export class GraphQLInputField extends GraphQLSchemaElement { name: string; description: Maybe; type: GraphQLInputType; - defaultValue: unknown; + defaultValue: GraphQLDefaultValueUsage | undefined; deprecationReason: Maybe; extensions: Readonly; astNode: Maybe; @@ -1640,7 +1664,7 @@ export class GraphQLInputField extends GraphQLSchemaElement { this.name = assertName(name); this.description = config.description; this.type = config.type; - this.defaultValue = config.defaultValue; + this.defaultValue = defineDefaultValue(coordinate, config); this.deprecationReason = config.deprecationReason; this.extensions = toObjMap(config.extensions); this.astNode = config.astNode; @@ -1654,7 +1678,8 @@ export class GraphQLInputField extends GraphQLSchemaElement { return { description: this.description, type: this.type, - defaultValue: this.defaultValue, + defaultValue: this.defaultValue?.value, + defaultValueLiteral: this.defaultValue?.literal, deprecationReason: this.deprecationReason, extensions: this.extensions, astNode: this.astNode, diff --git a/src/type/index.ts b/src/type/index.ts index e633914d960..9d47da09317 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -120,6 +120,7 @@ export type { GraphQLScalarSerializer, GraphQLScalarValueParser, GraphQLScalarLiteralParser, + GraphQLDefaultValueUsage, } from './definition.js'; export { diff --git a/src/type/introspection.ts b/src/type/introspection.ts index 15e17771ad8..be211cc6a33 100644 --- a/src/type/introspection.ts +++ b/src/type/introspection.ts @@ -395,8 +395,13 @@ export const __InputValue: GraphQLObjectType = new GraphQLObjectType({ 'A GraphQL-formatted string representing the default value for this input value.', resolve(inputValue) { const { type, defaultValue } = inputValue; - const valueAST = astFromValue(defaultValue, type); - return valueAST ? print(valueAST) : null; + if (!defaultValue) { + return null; + } + const literal = + defaultValue.literal ?? astFromValue(defaultValue.value, type); + invariant(literal != null, 'Invalid default value'); + return print(literal); }, }, isDeprecated: { diff --git a/src/utilities/TypeInfo.ts b/src/utilities/TypeInfo.ts index fe8ea1fbab9..716f6be39d5 100644 --- a/src/utilities/TypeInfo.ts +++ b/src/utilities/TypeInfo.ts @@ -9,6 +9,7 @@ import { getEnterLeaveForKind } from '../language/visitor.js'; import type { GraphQLArgument, GraphQLCompositeType, + GraphQLDefaultValueUsage, GraphQLEnumValue, GraphQLField, GraphQLInputField, @@ -43,7 +44,7 @@ export class TypeInfo { private _parentTypeStack: Array>; private _inputTypeStack: Array>; private _fieldDefStack: Array>>; - private _defaultValueStack: Array>; + private _defaultValueStack: Array; private _directive: Maybe; private _argument: Maybe; private _enumValue: Maybe; @@ -117,7 +118,7 @@ export class TypeInfo { } } - getDefaultValue(): Maybe { + getDefaultValue(): GraphQLDefaultValueUsage | undefined { if (this._defaultValueStack.length > 0) { return this._defaultValueStack[this._defaultValueStack.length - 1]; } diff --git a/src/utilities/__tests__/buildClientSchema-test.ts b/src/utilities/__tests__/buildClientSchema-test.ts index 2b0b0ae9399..54d8a7b12fb 100644 --- a/src/utilities/__tests__/buildClientSchema-test.ts +++ b/src/utilities/__tests__/buildClientSchema-test.ts @@ -445,6 +445,7 @@ describe('Type System: build schema from introspection', () => { } type Query { + defaultID(intArg: ID = "123"): String defaultInt(intArg: Int = 30): String defaultList(listArg: [Int] = [1, 2, 3]): String defaultObject(objArg: Geo = { lat: 37.485, lon: -122.148 }): String @@ -600,6 +601,28 @@ describe('Type System: build schema from introspection', () => { expect(result.data).to.deep.equal({ foo: 'bar' }); }); + it('can use client schema for execution if resolvers are added', () => { + const schema = buildSchema(` + type Query { + foo(bar: String = "abc"): String + } + `); + + const introspection = introspectionFromSchema(schema); + const clientSchema = buildClientSchema(introspection); + + const QueryType: GraphQLObjectType = clientSchema.getType('Query') as any; + QueryType.getFields().foo.resolve = (_value, args) => args.bar; + + const result = graphqlSync({ + schema: clientSchema, + source: '{ foo }', + }); + + expect(result.data).to.deep.equal({ foo: 'abc' }); + expect(result.data).to.deep.equal({ foo: 'abc' }); + }); + it('can build invalid schema', () => { const schema = buildSchema('type Query', { assumeValid: true }); diff --git a/src/utilities/__tests__/coerceInputValue-test.ts b/src/utilities/__tests__/coerceInputValue-test.ts index 2aa0fe7458f..b67f09db40d 100644 --- a/src/utilities/__tests__/coerceInputValue-test.ts +++ b/src/utilities/__tests__/coerceInputValue-test.ts @@ -5,6 +5,7 @@ import { identityFunc } from '../../jsutils/identityFunc.js'; import { invariant } from '../../jsutils/invariant.js'; import type { ObjMap } from '../../jsutils/ObjMap.js'; +import { Kind } from '../../language/kinds.js'; import { parseValue } from '../../language/parser.js'; import { print } from '../../language/printer.js'; @@ -24,7 +25,11 @@ import { GraphQLString, } from '../../type/scalars.js'; -import { coerceInputLiteral, coerceInputValue } from '../coerceInputValue.js'; +import { + coerceDefaultValue, + coerceInputLiteral, + coerceInputValue, +} from '../coerceInputValue.js'; interface CoerceResult { value: unknown; @@ -610,10 +615,14 @@ describe('coerceInputLiteral', () => { name: 'TestInput', fields: { int: { type: GraphQLInt, defaultValue: 42 }, + float: { + type: GraphQLFloat, + defaultValueLiteral: { kind: Kind.FLOAT, value: '3.14' }, + }, }, }); - test('{}', type, { int: 42 }); + test('{}', type, { int: 42, float: 3.14 }); }); const testInputObj = new GraphQLInputObjectType({ @@ -681,3 +690,26 @@ describe('coerceInputLiteral', () => { }); }); }); + +describe('coerceDefaultValue', () => { + it('memoizes coercion', () => { + const parseValueCalls: any = []; + + const spyScalar = new GraphQLScalarType({ + name: 'SpyScalar', + parseValue(value) { + parseValueCalls.push(value); + return value; + }, + }); + + const defaultValueUsage = { + literal: { kind: Kind.STRING, value: 'hello' }, + } as const; + expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello'); + + // Call a second time + expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello'); + expect(parseValueCalls).to.deep.equal(['hello']); + }); +}); diff --git a/src/utilities/astFromValue.ts b/src/utilities/astFromValue.ts index e1d994a8ee3..bb03baf232e 100644 --- a/src/utilities/astFromValue.ts +++ b/src/utilities/astFromValue.ts @@ -4,7 +4,7 @@ import { isIterableObject } from '../jsutils/isIterableObject.js'; import { isObjectLike } from '../jsutils/isObjectLike.js'; import type { Maybe } from '../jsutils/Maybe.js'; -import type { ObjectFieldNode, ValueNode } from '../language/ast.js'; +import type { ConstObjectFieldNode, ConstValueNode } from '../language/ast.js'; import { Kind } from '../language/kinds.js'; import type { GraphQLInputType } from '../type/definition.js'; @@ -41,7 +41,7 @@ import { GraphQLID } from '../type/scalars.js'; export function astFromValue( value: unknown, type: GraphQLInputType, -): Maybe { +): Maybe { if (isNonNullType(type)) { const astValue = astFromValue(value, type.ofType); if (astValue?.kind === Kind.NULL) { @@ -83,7 +83,7 @@ export function astFromValue( if (!isObjectLike(value)) { return null; } - const fieldNodes: Array = []; + const fieldNodes: Array = []; for (const field of Object.values(type.getFields())) { const fieldValue = astFromValue(value[field.name], field.type); if (fieldValue) { diff --git a/src/utilities/buildClientSchema.ts b/src/utilities/buildClientSchema.ts index 12800069643..ed24a80e1f8 100644 --- a/src/utilities/buildClientSchema.ts +++ b/src/utilities/buildClientSchema.ts @@ -32,7 +32,6 @@ import { specifiedScalarTypes } from '../type/scalars.js'; import type { GraphQLSchemaValidationOptions } from '../type/schema.js'; import { GraphQLSchema } from '../type/schema.js'; -import { coerceInputLiteral } from './coerceInputValue.js'; import type { IntrospectionDirective, IntrospectionEnumType, @@ -370,17 +369,13 @@ export function buildClientSchema( ); } - const defaultValue = - inputValueIntrospection.defaultValue != null - ? coerceInputLiteral( - parseConstValue(inputValueIntrospection.defaultValue), - type, - ) - : undefined; return { description: inputValueIntrospection.description, type, - defaultValue, + defaultValueLiteral: + inputValueIntrospection.defaultValue != null + ? parseConstValue(inputValueIntrospection.defaultValue) + : undefined, deprecationReason: inputValueIntrospection.deprecationReason, }; } diff --git a/src/utilities/coerceInputValue.ts b/src/utilities/coerceInputValue.ts index 556fb1bc5ad..b192ca7866c 100644 --- a/src/utilities/coerceInputValue.ts +++ b/src/utilities/coerceInputValue.ts @@ -17,7 +17,10 @@ import { GraphQLError } from '../error/GraphQLError.js'; import type { ValueNode } from '../language/ast'; import { Kind } from '../language/kinds.js'; -import type { GraphQLInputType } from '../type/definition.js'; +import type { + GraphQLDefaultValueUsage, + GraphQLInputType, +} from '../type/definition'; import { assertLeafType, isInputObjectType, @@ -111,8 +114,11 @@ function coerceInputValueImpl( const fieldValue = inputValue[field.name]; if (fieldValue === undefined) { - if (field.defaultValue !== undefined) { - coercedValue[field.name] = field.defaultValue; + if (field.defaultValue) { + coercedValue[field.name] = coerceDefaultValue( + field.defaultValue, + field.type, + ); } else if (isNonNullType(field.type)) { const typeStr = inspect(field.type); onError( @@ -273,8 +279,11 @@ export function coerceInputLiteral( if (isRequiredInputField(field)) { return; // Invalid: intentionally return no value. } - if (field.defaultValue !== undefined) { - coercedValue[field.name] = field.defaultValue; + if (field.defaultValue) { + coercedValue[field.name] = coerceDefaultValue( + field.defaultValue, + field.type, + ); } } else { const fieldValue = coerceInputLiteral( @@ -311,3 +320,23 @@ function isMissingVariable( (variables == null || !hasOwnProperty(variables, valueNode.name.value)) ); } + +/** + * @internal + */ +export function coerceDefaultValue( + defaultValue: GraphQLDefaultValueUsage, + type: GraphQLInputType, +): unknown { + // Memoize the result of coercing the default value in a hidden field. + let coercedValue = (defaultValue as any)._memoizedCoercedValue; + // istanbul ignore else (memoized case) + if (coercedValue === undefined) { + coercedValue = defaultValue.literal + ? coerceInputLiteral(defaultValue.literal, type) + : defaultValue.value; + invariant(coercedValue !== undefined); + (defaultValue as any)._memoizedCoercedValue = coercedValue; + } + return coercedValue; +} diff --git a/src/utilities/extendSchema.ts b/src/utilities/extendSchema.ts index 9a127b4bc38..299ce946d04 100644 --- a/src/utilities/extendSchema.ts +++ b/src/utilities/extendSchema.ts @@ -83,8 +83,6 @@ import { assertValidSDLExtension } from '../validation/validate.js'; import { getDirectiveValues } from '../execution/values.js'; -import { coerceInputLiteral } from './coerceInputValue.js'; - interface Options extends GraphQLSchemaValidationOptions { /** * Set to true to assume the SDL is valid. @@ -536,9 +534,7 @@ export function extendSchemaImpl( argConfigMap[arg.name.value] = { type, description: arg.description?.value, - defaultValue: arg.defaultValue - ? coerceInputLiteral(arg.defaultValue, type) - : undefined, + defaultValueLiteral: arg.defaultValue, deprecationReason: getDeprecationReason(arg), astNode: arg, }; @@ -565,9 +561,7 @@ export function extendSchemaImpl( inputFieldMap[field.name.value] = { type, description: field.description?.value, - defaultValue: field.defaultValue - ? coerceInputLiteral(field.defaultValue, type) - : undefined, + defaultValueLiteral: field.defaultValue, deprecationReason: getDeprecationReason(field), astNode: field, }; diff --git a/src/utilities/findBreakingChanges.ts b/src/utilities/findBreakingChanges.ts index 6b693046291..c636cd85b42 100644 --- a/src/utilities/findBreakingChanges.ts +++ b/src/utilities/findBreakingChanges.ts @@ -5,6 +5,7 @@ import { keyMap } from '../jsutils/keyMap.js'; import { print } from '../language/printer.js'; import type { + GraphQLDefaultValueUsage, GraphQLEnumType, GraphQLField, GraphQLInputObjectType, @@ -525,8 +526,11 @@ function typeKindName(type: GraphQLNamedType): string { invariant(false, 'Unexpected type: ' + inspect(type)); } -function stringifyValue(value: unknown, type: GraphQLInputType): string { - const ast = astFromValue(value, type); +function stringifyValue( + defaultValue: GraphQLDefaultValueUsage, + type: GraphQLInputType, +): string { + const ast = defaultValue.literal ?? astFromValue(defaultValue.value, type); invariant(ast != null); return print(sortValueNode(ast)); } diff --git a/src/utilities/printSchema.ts b/src/utilities/printSchema.ts index 527cf4be175..88ee7f0b4b3 100644 --- a/src/utilities/printSchema.ts +++ b/src/utilities/printSchema.ts @@ -261,10 +261,13 @@ function printArgs( } function printInputValue(arg: GraphQLInputField): string { - const defaultAST = astFromValue(arg.defaultValue, arg.type); let argDecl = arg.name + ': ' + String(arg.type); - if (defaultAST) { - argDecl += ` = ${print(defaultAST)}`; + if (arg.defaultValue) { + const literal = + arg.defaultValue.literal ?? + astFromValue(arg.defaultValue.value, arg.type); + invariant(literal != null, 'Invalid default value'); + argDecl += ` = ${print(literal)}`; } return argDecl + printDeprecated(arg.deprecationReason); } diff --git a/src/validation/ValidationContext.ts b/src/validation/ValidationContext.ts index b0c5524fa71..9550d1f187f 100644 --- a/src/validation/ValidationContext.ts +++ b/src/validation/ValidationContext.ts @@ -18,6 +18,7 @@ import { visit } from '../language/visitor.js'; import type { GraphQLArgument, GraphQLCompositeType, + GraphQLDefaultValueUsage, GraphQLEnumValue, GraphQLField, GraphQLInputType, @@ -32,7 +33,7 @@ type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode; interface VariableUsage { readonly node: VariableNode; readonly type: Maybe; - readonly defaultValue: Maybe; + readonly defaultValue: GraphQLDefaultValueUsage | undefined; } /** diff --git a/src/validation/rules/VariablesInAllowedPositionRule.ts b/src/validation/rules/VariablesInAllowedPositionRule.ts index 1a2c1d9c1c7..5a5f5097732 100644 --- a/src/validation/rules/VariablesInAllowedPositionRule.ts +++ b/src/validation/rules/VariablesInAllowedPositionRule.ts @@ -6,7 +6,10 @@ import type { ValueNode } from '../../language/ast.js'; import { Kind } from '../../language/kinds.js'; import type { ASTVisitor } from '../../language/visitor.js'; -import type { GraphQLType } from '../../type/definition.js'; +import type { + GraphQLDefaultValueUsage, + GraphQLType, +} from '../../type/definition.js'; import { isNonNullType } from '../../type/definition.js'; import type { GraphQLSchema } from '../../type/schema.js'; @@ -83,7 +86,7 @@ function allowedVariableUsage( varType: GraphQLType, varDefaultValue: Maybe, locationType: GraphQLType, - locationDefaultValue: Maybe, + locationDefaultValue: GraphQLDefaultValueUsage | undefined, ): boolean { if (isNonNullType(locationType) && !isNonNullType(varType)) { const hasNonNullVariableDefaultValue =