From a24b3003c5525ee8767a0b0a0c26d96e5963a6af Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Mon, 11 Dec 2017 14:58:58 -0800 Subject: [PATCH] Better Predicates Introduces new predicate and assertion functions for each kind of type mirroring the existing ones for the higher order types. It also includes predicates for directives and schema. This should remove the need to use `instanceof` in any usage of GraphQL.js This consolidates the checking of instances which generalizes the cross-realm/multiple-module check so that the clearer error message is provided in nearly all scenarios, reducing confusion. This also replaces nearly all `instanceof` with new predicate functions internally. Fixes #1134 --- src/execution/__tests__/executor-test.js | 2 +- src/execution/execute.js | 20 +- src/execution/values.js | 10 +- src/index.js | 23 + src/jsutils/instanceOf.js | 44 ++ src/jsutils/invariant.js | 1 + src/subscription/__tests__/subscribe-test.js | 4 +- src/type/__tests__/definition-test.js | 20 +- src/type/__tests__/predicate-test.js | 472 ++++++++++++++++++ src/type/definition.js | 256 +++++++--- src/type/directives.js | 12 + src/type/index.js | 31 +- src/type/introspection.js | 39 +- src/type/schema.js | 43 +- src/type/validate.js | 60 +-- src/type/wrappers.js | 14 +- src/utilities/TypeInfo.js | 21 +- src/utilities/__tests__/extendSchema-test.js | 9 +- src/utilities/astFromValue.js | 88 ++-- src/utilities/buildASTSchema.js | 13 +- src/utilities/buildClientSchema.js | 21 +- src/utilities/coerceValue.js | 24 +- src/utilities/extendSchema.js | 24 +- src/utilities/findBreakingChanges.js | 107 ++-- src/utilities/isValidLiteralValue.js | 45 +- src/utilities/schemaPrinter.js | 32 +- src/utilities/typeComparators.js | 28 +- src/utilities/typeFromAST.js | 8 +- src/utilities/valueFromAST.js | 53 +- src/utilities/valueFromASTUntyped.js | 4 +- .../rules/DefaultValuesOfCorrectType.js | 4 +- src/validation/rules/FieldsOnCorrectType.js | 9 +- .../rules/OverlappingFieldsCanBeMerged.js | 36 +- .../rules/ProvidedNonNullArguments.js | 6 +- .../rules/VariablesInAllowedPosition.js | 3 +- 35 files changed, 1136 insertions(+), 450 deletions(-) create mode 100644 src/jsutils/instanceOf.js create mode 100644 src/type/__tests__/predicate-test.js diff --git a/src/execution/__tests__/executor-test.js b/src/execution/__tests__/executor-test.js index c70d50abe5..e9110866ad 100644 --- a/src/execution/__tests__/executor-test.js +++ b/src/execution/__tests__/executor-test.js @@ -39,7 +39,7 @@ describe('Execute: Handles basic execution tasks', () => { execute({ document: parse('{ field }'), }), - ).to.throw('Must provide schema'); + ).to.throw('Expected undefined to be a GraphQL schema.'); }); it('accepts an object with named properties as arguments', async () => { diff --git a/src/execution/execute.js b/src/execution/execute.js index e217c9b071..7c096574fc 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -22,12 +22,15 @@ import { getDirectiveValues, } from './values'; import { - GraphQLObjectType, + isObjectType, isAbstractType, isLeafType, + isListType, + isNonNullType, } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; +import type { GraphQLList } from '../type/wrappers'; import type { + GraphQLObjectType, GraphQLOutputType, GraphQLLeafType, GraphQLAbstractType, @@ -807,7 +810,7 @@ function completeValueCatchingError( ): mixed { // If the field type is non-nullable, then it is resolved without any // protection from errors, however it still properly locates the error. - if (returnType instanceof GraphQLNonNull) { + if (isNonNullType(returnType)) { return completeValueWithLocatedError( exeContext, returnType, @@ -934,7 +937,7 @@ function completeValue( // If field type is NonNull, complete for inner type, and throw field error // if result is null. - if (returnType instanceof GraphQLNonNull) { + if (isNonNullType(returnType)) { const completed = completeValue( exeContext, returnType.ofType, @@ -959,7 +962,7 @@ function completeValue( } // If field type is List, complete each item in the list with the inner type - if (returnType instanceof GraphQLList) { + if (isListType(returnType)) { return completeListValue( exeContext, returnType, @@ -990,7 +993,7 @@ function completeValue( } // If field type is Object, execute and complete all sub-selections. - if (returnType instanceof GraphQLObjectType) { + if (isObjectType(returnType)) { return completeObjectValue( exeContext, returnType, @@ -1002,6 +1005,7 @@ function completeValue( } // Not reachable. All possible output types have been considered. + /* istanbul ignore next */ throw new Error( `Cannot complete value of unexpected type "${String( (returnType: empty), @@ -1015,7 +1019,7 @@ function completeValue( */ function completeListValue( exeContext: ExecutionContext, - returnType: GraphQLList<*>, + returnType: GraphQLList, fieldNodes: $ReadOnlyArray, info: GraphQLResolveInfo, path: ResponsePath, @@ -1138,7 +1142,7 @@ function ensureValidRuntimeType( ? exeContext.schema.getType(runtimeTypeOrName) : runtimeTypeOrName; - if (!(runtimeType instanceof GraphQLObjectType)) { + if (!isObjectType(runtimeType)) { throw new GraphQLError( `Abstract type ${returnType.name} must resolve to an Object type at ` + `runtime for field ${info.parentType.name}.${info.fieldName} with ` + diff --git a/src/execution/values.js b/src/execution/values.js index 1423720442..b914ce810d 100644 --- a/src/execution/values.js +++ b/src/execution/values.js @@ -17,9 +17,7 @@ import { valueFromAST } from '../utilities/valueFromAST'; import { isValidLiteralValue } from '../utilities/isValidLiteralValue'; import * as Kind from '../language/kinds'; import { print } from '../language/printer'; -import { isInputType } from '../type/definition'; -import { GraphQLNonNull } from '../type/wrappers'; - +import { isInputType, isNonNullType } from '../type/definition'; import type { ObjMap } from '../jsutils/ObjMap'; import type { GraphQLField } from '../type/definition'; import type { GraphQLDirective } from '../type/directives'; @@ -69,7 +67,7 @@ export function getVariableValues( } else { const value = inputs[varName]; if (isInvalid(value)) { - if (varType instanceof GraphQLNonNull) { + if (isNonNullType(varType)) { errors.push( new GraphQLError( `Variable "$${varName}" of required type ` + @@ -134,7 +132,7 @@ export function getArgumentValues( if (!argumentNode) { if (!isInvalid(defaultValue)) { coercedValues[name] = defaultValue; - } else if (argType instanceof GraphQLNonNull) { + } else if (isNonNullType(argType)) { throw new GraphQLError( `Argument "${name}" of required type ` + `"${String(argType)}" was not provided.`, @@ -154,7 +152,7 @@ export function getArgumentValues( coercedValues[name] = variableValues[variableName]; } else if (!isInvalid(defaultValue)) { coercedValues[name] = defaultValue; - } else if (argType instanceof GraphQLNonNull) { + } else if (isNonNullType(argType)) { throw new GraphQLError( `Argument "${name}" of required type "${String(argType)}" was ` + `provided the variable "$${variableName}" which was not provided ` + diff --git a/src/index.js b/src/index.js index b595e5047f..9955d56509 100644 --- a/src/index.js +++ b/src/index.js @@ -79,23 +79,45 @@ export { __EnumValue, __TypeKind, // Predicates + isSchema, + isDirective, isType, + isScalarType, + isObjectType, + isInterfaceType, + isUnionType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, isInputType, isOutputType, isLeafType, isCompositeType, isAbstractType, + isWrappingType, + isNullableType, isNamedType, isSpecifiedScalarType, isIntrospectionType, isSpecifiedDirective, // Assertions assertType, + assertScalarType, + assertObjectType, + assertInterfaceType, + assertUnionType, + assertEnumType, + assertInputObjectType, + assertListType, + assertNonNullType, assertInputType, assertOutputType, assertLeafType, assertCompositeType, assertAbstractType, + assertWrappingType, + assertNullableType, assertNamedType, // Un-modifiers getNullableType, @@ -112,6 +134,7 @@ export type { GraphQLLeafType, GraphQLCompositeType, GraphQLAbstractType, + GraphQLWrappingType, GraphQLNullableType, GraphQLNamedType, Thunk, diff --git a/src/jsutils/instanceOf.js b/src/jsutils/instanceOf.js new file mode 100644 index 0000000000..8c37778360 --- /dev/null +++ b/src/jsutils/instanceOf.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +/** + * A replacement for instanceof which includes an error warning when multi-realm + * constructors are detected. + */ +declare function instanceOf( + value: mixed, + constructor: mixed, +): boolean %checks(value instanceof constructor); + +// eslint-disable-next-line no-redeclare +export default function instanceOf(value, constructor) { + if (value instanceof constructor) { + return true; + } + if (value) { + const valueConstructor = value.constructor; + if (valueConstructor && valueConstructor.name === constructor.name) { + throw new Error( + `Cannot use ${constructor.name} "${value}" from another module or realm. + +Ensure that there is only one instance of "graphql" in the node_modules +directory. If different versions of "graphql" are the dependencies of other +relied on modules, use "resolutions" to ensure only one version is installed. + +https://yarnpkg.com/en/docs/selective-version-resolutions + +Duplicate "graphql" modules cannot be used at the same time since different +versions may have different capabilities and behavior. The data from one +version used in the function from another could produce confusing and +spurious results.`, + ); + } + } + return false; +} diff --git a/src/jsutils/invariant.js b/src/jsutils/invariant.js index 0b60045356..8704978fb3 100644 --- a/src/jsutils/invariant.js +++ b/src/jsutils/invariant.js @@ -8,6 +8,7 @@ */ export default function invariant(condition: mixed, message: string) { + /* istanbul ignore else */ if (!condition) { throw new Error(message); } diff --git a/src/subscription/__tests__/subscribe-test.js b/src/subscription/__tests__/subscribe-test.js index 5d0525521a..141733de36 100644 --- a/src/subscription/__tests__/subscribe-test.js +++ b/src/subscription/__tests__/subscribe-test.js @@ -313,12 +313,12 @@ describe('Subscription Initialization Phase', () => { await expectPromiseToThrow( () => subscribe(null, document), - 'Must provide schema.', + 'Expected null to be a GraphQL schema.', ); await expectPromiseToThrow( () => subscribe({ document }), - 'Must provide schema.', + 'Expected undefined to be a GraphQL schema.', ); }); diff --git a/src/type/__tests__/definition-test.js b/src/type/__tests__/definition-test.js index 68c8fde83f..da8aec0841 100644 --- a/src/type/__tests__/definition-test.js +++ b/src/type/__tests__/definition-test.js @@ -23,7 +23,7 @@ import { import { describe, it } from 'mocha'; import { expect } from 'chai'; -import { isInputType, isOutputType } from '../definition'; +import { isObjectType, isInputType, isOutputType } from '../definition'; const BlogImage = new GraphQLObjectType({ name: 'Image', @@ -118,19 +118,19 @@ describe('Type System: Example', () => { const articleFieldType = articleField ? articleField.type : null; const titleField = - articleFieldType instanceof GraphQLObjectType && + isObjectType(articleFieldType) && articleFieldType.getFields()[('title': string)]; expect(titleField && titleField.name).to.equal('title'); expect(titleField && titleField.type).to.equal(GraphQLString); expect(titleField && titleField.type.name).to.equal('String'); const authorField = - articleFieldType instanceof GraphQLObjectType && + isObjectType(articleFieldType) && articleFieldType.getFields()[('author': string)]; const authorFieldType = authorField ? authorField.type : null; const recentArticleField = - authorFieldType instanceof GraphQLObjectType && + isObjectType(authorFieldType) && authorFieldType.getFields()[('recentArticle': string)]; expect(recentArticleField && recentArticleField.type).to.equal(BlogArticle); @@ -374,12 +374,6 @@ describe('Type System: Example', () => { }); }); - it('prohibits nesting NonNull inside NonNull', () => { - expect(() => GraphQLNonNull(GraphQLNonNull(GraphQLInt))).to.throw( - 'Can only create NonNull of a Nullable GraphQLType but got: Int!.', - ); - }); - it('prohibits putting non-Object types in unions', () => { const badUnionTypes = [ GraphQLInt, @@ -476,7 +470,7 @@ describe('Type System: Example', () => { }); }); -describe('Type System: List must accept GraphQL types', () => { +describe('Type System: List must accept only types', () => { const types = [ GraphQLString, ScalarType, @@ -506,7 +500,7 @@ describe('Type System: List must accept GraphQL types', () => { }); }); -describe('Type System: NonNull must accept GraphQL types', () => { +describe('Type System: NonNull must only accept non-nullable types', () => { const nullableTypes = [ GraphQLString, ScalarType, @@ -536,7 +530,7 @@ describe('Type System: NonNull must accept GraphQL types', () => { notNullableTypes.forEach(type => { it(`rejects a non-type as nullable type of non-null: ${type}`, () => { expect(() => GraphQLNonNull(type)).to.throw( - `Can only create NonNull of a Nullable GraphQLType but got: ${type}.`, + `Expected ${type} to be a GraphQL nullable type.`, ); }); }); diff --git a/src/type/__tests__/predicate-test.js b/src/type/__tests__/predicate-test.js new file mode 100644 index 0000000000..148e42e240 --- /dev/null +++ b/src/type/__tests__/predicate-test.js @@ -0,0 +1,472 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { describe, it } from 'mocha'; +import { expect } from 'chai'; + +import { + GraphQLScalarType, + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, + GraphQLObjectType, + GraphQLUnionType, + GraphQLList, + GraphQLNonNull, + GraphQLString, + isType, + isScalarType, + isObjectType, + isInterfaceType, + isUnionType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, + isInputType, + isOutputType, + isLeafType, + isCompositeType, + isAbstractType, + isWrappingType, + isNullableType, + isNamedType, + assertType, + assertScalarType, + assertObjectType, + assertInterfaceType, + assertUnionType, + assertEnumType, + assertInputObjectType, + assertListType, + assertNonNullType, + assertInputType, + assertOutputType, + assertLeafType, + assertCompositeType, + assertAbstractType, + assertWrappingType, + assertNullableType, + assertNamedType, + getNullableType, + getNamedType, +} from '../'; + +const ObjectType = new GraphQLObjectType({ name: 'Object' }); +const InterfaceType = new GraphQLInterfaceType({ name: 'Interface' }); +const UnionType = new GraphQLUnionType({ name: 'Union', types: [ObjectType] }); +const EnumType = new GraphQLEnumType({ name: 'Enum', values: { foo: {} } }); +const InputObjectType = new GraphQLInputObjectType({ name: 'InputObject' }); +const ScalarType = new GraphQLScalarType({ + name: 'Scalar', + serialize() {}, + parseValue() {}, + parseLiteral() {}, +}); + +describe('Type predicates', () => { + describe('isType', () => { + it('returns true for unwrapped types', () => { + expect(isType(GraphQLString)).to.equal(true); + expect(() => assertType(GraphQLString)).not.to.throw(); + expect(isType(ObjectType)).to.equal(true); + expect(() => assertType(ObjectType)).not.to.throw(); + }); + + it('returns true for wrapped types', () => { + expect(isType(GraphQLNonNull(GraphQLString))).to.equal(true); + expect(() => assertType(GraphQLNonNull(GraphQLString))).not.to.throw(); + }); + + it('returns false for type classes (rather than instances)', () => { + expect(isType(GraphQLObjectType)).to.equal(false); + expect(() => assertType(GraphQLObjectType)).to.throw(); + }); + + it('returns false for random garbage', () => { + expect(isType({ what: 'is this' })).to.equal(false); + expect(() => assertType({ what: 'is this' })).to.throw(); + }); + }); + + describe('isScalarType', () => { + it('returns true for spec defined scalar', () => { + expect(isScalarType(GraphQLString)).to.equal(true); + expect(() => assertScalarType(GraphQLString)).not.to.throw(); + }); + + it('returns true for custom scalar', () => { + expect(isScalarType(ScalarType)).to.equal(true); + expect(() => assertScalarType(ScalarType)).not.to.throw(); + }); + + it('returns false for wrapped scalar', () => { + expect(isScalarType(GraphQLList(ScalarType))).to.equal(false); + expect(() => assertScalarType(GraphQLList(ScalarType))).to.throw(); + }); + + it('returns false for non-scalar', () => { + expect(isScalarType(EnumType)).to.equal(false); + expect(() => assertScalarType(EnumType)).to.throw(); + }); + }); + + describe('isObjectType', () => { + it('returns true for object type', () => { + expect(isObjectType(ObjectType)).to.equal(true); + expect(() => assertObjectType(ObjectType)).not.to.throw(); + }); + + it('returns false for wrapped object type', () => { + expect(isObjectType(GraphQLList(ObjectType))).to.equal(false); + expect(() => assertObjectType(GraphQLList(ObjectType))).to.throw(); + }); + + it('returns false for non-object type', () => { + expect(isObjectType(InterfaceType)).to.equal(false); + expect(() => assertObjectType(InterfaceType)).to.throw(); + }); + }); + + describe('isInterfaceType', () => { + it('returns true for interface type', () => { + expect(isInterfaceType(InterfaceType)).to.equal(true); + expect(() => assertInterfaceType(InterfaceType)).not.to.throw(); + }); + + it('returns false for wrapped interface type', () => { + expect(isInterfaceType(GraphQLList(InterfaceType))).to.equal(false); + expect(() => assertInterfaceType(GraphQLList(InterfaceType))).to.throw(); + }); + + it('returns false for non-interface type', () => { + expect(isInterfaceType(ObjectType)).to.equal(false); + expect(() => assertInterfaceType(ObjectType)).to.throw(); + }); + }); + + describe('isUnionType', () => { + it('returns true for union type', () => { + expect(isUnionType(UnionType)).to.equal(true); + expect(() => assertUnionType(UnionType)).not.to.throw(); + }); + + it('returns false for wrapped union type', () => { + expect(isUnionType(GraphQLList(UnionType))).to.equal(false); + expect(() => assertUnionType(GraphQLList(UnionType))).to.throw(); + }); + + it('returns false for non-union type', () => { + expect(isUnionType(ObjectType)).to.equal(false); + expect(() => assertUnionType(ObjectType)).to.throw(); + }); + }); + + describe('isEnumType', () => { + it('returns true for enum type', () => { + expect(isEnumType(EnumType)).to.equal(true); + expect(() => assertEnumType(EnumType)).not.to.throw(); + }); + + it('returns false for wrapped enum type', () => { + expect(isEnumType(GraphQLList(EnumType))).to.equal(false); + expect(() => assertEnumType(GraphQLList(EnumType))).to.throw(); + }); + + it('returns false for non-enum type', () => { + expect(isEnumType(ScalarType)).to.equal(false); + expect(() => assertEnumType(ScalarType)).to.throw(); + }); + }); + + describe('isInputObjectType', () => { + it('returns true for input object type', () => { + expect(isInputObjectType(InputObjectType)).to.equal(true); + expect(() => assertInputObjectType(InputObjectType)).not.to.throw(); + }); + + it('returns false for wrapped input object type', () => { + expect(isInputObjectType(GraphQLList(InputObjectType))).to.equal(false); + expect(() => + assertInputObjectType(GraphQLList(InputObjectType)), + ).to.throw(); + }); + + it('returns false for non-input-object type', () => { + expect(isInputObjectType(ObjectType)).to.equal(false); + expect(() => assertInputObjectType(ObjectType)).to.throw(); + }); + }); + + describe('isListType', () => { + it('returns true for a list wrapped type', () => { + expect(isListType(GraphQLList(ObjectType))).to.equal(true); + expect(() => assertListType(GraphQLList(ObjectType))).not.to.throw(); + }); + + it('returns false for an unwrapped type', () => { + expect(isListType(ObjectType)).to.equal(false); + expect(() => assertListType(ObjectType)).to.throw(); + }); + + it('returns true for a non-list wrapped type', () => { + expect(isListType(GraphQLNonNull(GraphQLList(ObjectType)))).to.equal( + false, + ); + expect(() => + assertListType(GraphQLNonNull(GraphQLList(ObjectType))), + ).to.throw(); + }); + }); + + describe('isNonNullType', () => { + it('returns true for a non-null wrapped type', () => { + expect(isNonNullType(GraphQLNonNull(ObjectType))).to.equal(true); + expect(() => + assertNonNullType(GraphQLNonNull(ObjectType)), + ).not.to.throw(); + }); + + it('returns false for an unwrapped type', () => { + expect(isNonNullType(ObjectType)).to.equal(false); + expect(() => assertNonNullType(ObjectType)).to.throw(); + }); + + it('returns true for a not non-null wrapped type', () => { + expect(isNonNullType(GraphQLList(GraphQLNonNull(ObjectType)))).to.equal( + false, + ); + expect(() => + assertNonNullType(GraphQLList(GraphQLNonNull(ObjectType))), + ).to.throw(); + }); + }); + + describe('isInputType', () => { + it('returns true for an input type', () => { + expect(isInputType(InputObjectType)).to.equal(true); + expect(() => assertInputType(InputObjectType)).not.to.throw(); + }); + + it('returns true for a wrapped input type', () => { + expect(isInputType(GraphQLList(InputObjectType))).to.equal(true); + expect(() => + assertInputType(GraphQLList(InputObjectType)), + ).not.to.throw(); + expect(isInputType(GraphQLNonNull(InputObjectType))).to.equal(true); + expect(() => + assertInputType(GraphQLNonNull(InputObjectType)), + ).not.to.throw(); + }); + + it('returns false for an output type', () => { + expect(isInputType(ObjectType)).to.equal(false); + expect(() => assertInputType(ObjectType)).to.throw(); + }); + + it('returns false for a wrapped output type', () => { + expect(isInputType(GraphQLList(ObjectType))).to.equal(false); + expect(() => assertInputType(GraphQLList(ObjectType))).to.throw(); + expect(isInputType(GraphQLNonNull(ObjectType))).to.equal(false); + expect(() => assertInputType(GraphQLNonNull(ObjectType))).to.throw(); + }); + }); + + describe('isOutputType', () => { + it('returns true for an output type', () => { + expect(isOutputType(ObjectType)).to.equal(true); + expect(() => assertOutputType(ObjectType)).not.to.throw(); + }); + + it('returns true for a wrapped output type', () => { + expect(isOutputType(GraphQLList(ObjectType))).to.equal(true); + expect(() => assertOutputType(GraphQLList(ObjectType))).not.to.throw(); + expect(isOutputType(GraphQLNonNull(ObjectType))).to.equal(true); + expect(() => assertOutputType(GraphQLNonNull(ObjectType))).not.to.throw(); + }); + + it('returns false for an input type', () => { + expect(isOutputType(InputObjectType)).to.equal(false); + expect(() => assertOutputType(InputObjectType)).to.throw(); + }); + + it('returns false for a wrapped input type', () => { + expect(isOutputType(GraphQLList(InputObjectType))).to.equal(false); + expect(() => assertOutputType(GraphQLList(InputObjectType))).to.throw(); + expect(isOutputType(GraphQLNonNull(InputObjectType))).to.equal(false); + expect(() => + assertOutputType(GraphQLNonNull(InputObjectType)), + ).to.throw(); + }); + }); + + describe('isLeafType', () => { + it('returns true for scalar and enum types', () => { + expect(isLeafType(ScalarType)).to.equal(true); + expect(() => assertLeafType(ScalarType)).not.to.throw(); + expect(isLeafType(EnumType)).to.equal(true); + expect(() => assertLeafType(EnumType)).not.to.throw(); + }); + + it('returns false for wrapped leaf type', () => { + expect(isLeafType(GraphQLList(ScalarType))).to.equal(false); + expect(() => assertLeafType(GraphQLList(ScalarType))).to.throw(); + }); + + it('returns false for non-leaf type', () => { + expect(isLeafType(ObjectType)).to.equal(false); + expect(() => assertLeafType(ObjectType)).to.throw(); + }); + + it('returns false for wrapped non-leaf type', () => { + expect(isLeafType(GraphQLList(ObjectType))).to.equal(false); + expect(() => assertLeafType(GraphQLList(ObjectType))).to.throw(); + }); + }); + + describe('isCompositeType', () => { + it('returns true for object, interface, and union types', () => { + expect(isCompositeType(ObjectType)).to.equal(true); + expect(() => assertCompositeType(ObjectType)).not.to.throw(); + expect(isCompositeType(InterfaceType)).to.equal(true); + expect(() => assertCompositeType(InterfaceType)).not.to.throw(); + expect(isCompositeType(UnionType)).to.equal(true); + expect(() => assertCompositeType(UnionType)).not.to.throw(); + }); + + it('returns false for wrapped composite type', () => { + expect(isCompositeType(GraphQLList(ObjectType))).to.equal(false); + expect(() => assertCompositeType(GraphQLList(ObjectType))).to.throw(); + }); + + it('returns false for non-composite type', () => { + expect(isCompositeType(InputObjectType)).to.equal(false); + expect(() => assertCompositeType(InputObjectType)).to.throw(); + }); + + it('returns false for wrapped non-composite type', () => { + expect(isCompositeType(GraphQLList(InputObjectType))).to.equal(false); + expect(() => + assertCompositeType(GraphQLList(InputObjectType)), + ).to.throw(); + }); + }); + + describe('isAbstractType', () => { + it('returns true for interface and union types', () => { + expect(isAbstractType(InterfaceType)).to.equal(true); + expect(() => assertAbstractType(InterfaceType)).not.to.throw(); + expect(isAbstractType(UnionType)).to.equal(true); + expect(() => assertAbstractType(UnionType)).not.to.throw(); + }); + + it('returns false for wrapped abstract type', () => { + expect(isAbstractType(GraphQLList(InterfaceType))).to.equal(false); + expect(() => assertAbstractType(GraphQLList(InterfaceType))).to.throw(); + }); + + it('returns false for non-abstract type', () => { + expect(isAbstractType(ObjectType)).to.equal(false); + expect(() => assertAbstractType(ObjectType)).to.throw(); + }); + + it('returns false for wrapped non-abstract type', () => { + expect(isAbstractType(GraphQLList(ObjectType))).to.equal(false); + expect(() => assertAbstractType(GraphQLList(ObjectType))).to.throw(); + }); + }); + + describe('isWrappingType', () => { + it('returns true for list and non-null types', () => { + expect(isWrappingType(GraphQLList(ObjectType))).to.equal(true); + expect(() => assertWrappingType(GraphQLList(ObjectType))).not.to.throw(); + expect(isWrappingType(GraphQLNonNull(ObjectType))).to.equal(true); + expect(() => + assertWrappingType(GraphQLNonNull(ObjectType)), + ).not.to.throw(); + }); + + it('returns false for unwrapped types', () => { + expect(isWrappingType(ObjectType)).to.equal(false); + expect(() => assertWrappingType(ObjectType)).to.throw(); + }); + }); + + describe('isNullableType', () => { + it('returns true for unwrapped types', () => { + expect(isNullableType(ObjectType)).to.equal(true); + expect(() => assertNullableType(ObjectType)).not.to.throw(); + }); + + it('returns true for list of non-null types', () => { + expect(isNullableType(GraphQLList(GraphQLNonNull(ObjectType)))).to.equal( + true, + ); + expect(() => + assertNullableType(GraphQLList(GraphQLNonNull(ObjectType))), + ).not.to.throw(); + }); + + it('returns false for non-null types', () => { + expect(isNullableType(GraphQLNonNull(ObjectType))).to.equal(false); + expect(() => assertNullableType(GraphQLNonNull(ObjectType))).to.throw(); + }); + }); + + describe('getNullableType', () => { + it('returns undefined for no type', () => { + expect(getNullableType()).to.equal(undefined); + expect(getNullableType(null)).to.equal(undefined); + }); + + it('returns self for a nullable type', () => { + expect(getNullableType(ObjectType)).to.equal(ObjectType); + const listOfObj = GraphQLList(ObjectType); + expect(getNullableType(listOfObj)).to.equal(listOfObj); + }); + + it('unwraps non-null type', () => { + expect(getNullableType(GraphQLNonNull(ObjectType))).to.equal(ObjectType); + }); + }); + + describe('isNamedType', () => { + it('returns true for unwrapped types', () => { + expect(isNamedType(ObjectType)).to.equal(true); + expect(() => assertNamedType(ObjectType)).not.to.throw(); + }); + + it('returns false for list and non-null types', () => { + expect(isNamedType(GraphQLList(ObjectType))).to.equal(false); + expect(() => assertNamedType(GraphQLList(ObjectType))).to.throw(); + expect(isNamedType(GraphQLNonNull(ObjectType))).to.equal(false); + expect(() => assertNamedType(GraphQLNonNull(ObjectType))).to.throw(); + }); + }); + + describe('getNamedType', () => { + it('returns undefined for no type', () => { + expect(getNamedType()).to.equal(undefined); + expect(getNamedType(null)).to.equal(undefined); + }); + + it('returns self for a unwrapped type', () => { + expect(getNamedType(ObjectType)).to.equal(ObjectType); + }); + + it('unwraps wrapper types', () => { + expect(getNamedType(GraphQLNonNull(ObjectType))).to.equal(ObjectType); + expect(getNamedType(GraphQLList(ObjectType))).to.equal(ObjectType); + }); + + it('unwraps deeply wrapper types', () => { + expect( + getNamedType(GraphQLNonNull(GraphQLList(GraphQLNonNull(ObjectType)))), + ).to.equal(ObjectType); + }); + }); +}); diff --git a/src/type/definition.js b/src/type/definition.js index b4c78b2671..3107904ba6 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -7,6 +7,7 @@ * @flow */ +import instanceOf from '../jsutils/instanceOf'; import invariant from '../jsutils/invariant'; import isInvalid from '../jsutils/isInvalid'; import type { ObjMap } from '../jsutils/ObjMap'; @@ -50,14 +51,14 @@ export type GraphQLType = export function isType(type: mixed): boolean %checks { return ( - type instanceof GraphQLScalarType || - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType || - type instanceof GraphQLUnionType || - type instanceof GraphQLEnumType || - type instanceof GraphQLInputObjectType || - type instanceof GraphQLList || - type instanceof GraphQLNonNull + isScalarType(type) || + isObjectType(type) || + isInterfaceType(type) || + isUnionType(type) || + isEnumType(type) || + isInputObjectType(type) || + isListType(type) || + isNonNullType(type) ); } @@ -66,6 +67,130 @@ export function assertType(type: mixed): GraphQLType { return (type: any); } +/** + * There are predicates for each kind of GraphQL type. + */ + +declare function isScalarType(type: mixed): boolean %checks(type instanceof + GraphQLScalarType); +// eslint-disable-next-line no-redeclare +export function isScalarType(type) { + return instanceOf(type, GraphQLScalarType); +} + +export function assertScalarType(type: mixed): GraphQLScalarType { + invariant( + isScalarType(type), + `Expected ${String(type)} to be a GraphQL Scalar type.`, + ); + return type; +} + +declare function isObjectType(type: mixed): boolean %checks(type instanceof + GraphQLObjectType); +// eslint-disable-next-line no-redeclare +export function isObjectType(type) { + return instanceOf(type, GraphQLObjectType); +} + +export function assertObjectType(type: mixed): GraphQLObjectType { + invariant( + isObjectType(type), + `Expected ${String(type)} to be a GraphQL Object type.`, + ); + return type; +} + +declare function isInterfaceType(type: mixed): boolean %checks(type instanceof + GraphQLInterfaceType); +// eslint-disable-next-line no-redeclare +export function isInterfaceType(type) { + return instanceOf(type, GraphQLInterfaceType); +} + +export function assertInterfaceType(type: mixed): GraphQLInterfaceType { + invariant( + isInterfaceType(type), + `Expected ${String(type)} to be a GraphQL Interface type.`, + ); + return type; +} + +declare function isUnionType(type: mixed): boolean %checks(type instanceof + GraphQLUnionType); +// eslint-disable-next-line no-redeclare +export function isUnionType(type) { + return instanceOf(type, GraphQLUnionType); +} + +export function assertUnionType(type: mixed): GraphQLUnionType { + invariant( + isUnionType(type), + `Expected ${String(type)} to be a GraphQL Union type.`, + ); + return type; +} + +declare function isEnumType(type: mixed): boolean %checks(type instanceof + GraphQLEnumType); +// eslint-disable-next-line no-redeclare +export function isEnumType(type) { + return instanceOf(type, GraphQLEnumType); +} + +export function assertEnumType(type: mixed): GraphQLEnumType { + invariant( + isEnumType(type), + `Expected ${String(type)} to be a GraphQL Enum type.`, + ); + return type; +} + +declare function isInputObjectType(type: mixed): boolean %checks(type instanceof + GraphQLInputObjectType); +// eslint-disable-next-line no-redeclare +export function isInputObjectType(type) { + return instanceOf(type, GraphQLInputObjectType); +} + +export function assertInputObjectType(type: mixed): GraphQLInputObjectType { + invariant( + isInputObjectType(type), + `Expected ${String(type)} to be a GraphQL Input Object type.`, + ); + return type; +} + +declare function isListType(type: mixed): boolean %checks(type instanceof + GraphQLList); +// eslint-disable-next-line no-redeclare +export function isListType(type) { + return instanceOf(type, GraphQLList); +} + +export function assertListType(type: mixed): GraphQLList { + invariant( + isListType(type), + `Expected ${String(type)} to be a GraphQL List type.`, + ); + return type; +} + +declare function isNonNullType(type: mixed): boolean %checks(type instanceof + GraphQLNonNull); +// eslint-disable-next-line no-redeclare +export function isNonNullType(type) { + return instanceOf(type, GraphQLNonNull); +} + +export function assertNonNullType(type: mixed): GraphQLNonNull { + invariant( + isNonNullType(type), + `Expected ${String(type)} to be a GraphQL Non-Null type.`, + ); + return type; +} + /** * These types may be used as input types for arguments and directives. */ @@ -81,17 +206,16 @@ export type GraphQLInputType = | GraphQLList, >; -export function isInputType(type: ?GraphQLType): boolean %checks { +export function isInputType(type: mixed): boolean %checks { return ( - type instanceof GraphQLScalarType || - type instanceof GraphQLEnumType || - type instanceof GraphQLInputObjectType || - (type instanceof GraphQLNonNull && isInputType(type.ofType)) || - (type instanceof GraphQLList && isInputType(type.ofType)) + isScalarType(type) || + isEnumType(type) || + isInputObjectType(type) || + (isWrappingType(type) && isInputType(type.ofType)) ); } -export function assertInputType(type: ?GraphQLType): GraphQLInputType { +export function assertInputType(type: mixed): GraphQLInputType { invariant( isInputType(type), `Expected ${String(type)} to be a GraphQL input type.`, @@ -118,19 +242,18 @@ export type GraphQLOutputType = | GraphQLList, >; -export function isOutputType(type: ?GraphQLType): boolean %checks { +export function isOutputType(type: mixed): boolean %checks { return ( - type instanceof GraphQLScalarType || - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType || - type instanceof GraphQLUnionType || - type instanceof GraphQLEnumType || - (type instanceof GraphQLNonNull && isOutputType(type.ofType)) || - (type instanceof GraphQLList && isOutputType(type.ofType)) + isScalarType(type) || + isObjectType(type) || + isInterfaceType(type) || + isUnionType(type) || + isEnumType(type) || + (isWrappingType(type) && isOutputType(type.ofType)) ); } -export function assertOutputType(type: ?GraphQLType): GraphQLOutputType { +export function assertOutputType(type: mixed): GraphQLOutputType { invariant( isOutputType(type), `Expected ${String(type)} to be a GraphQL output type.`, @@ -143,11 +266,11 @@ export function assertOutputType(type: ?GraphQLType): GraphQLOutputType { */ export type GraphQLLeafType = GraphQLScalarType | GraphQLEnumType; -export function isLeafType(type: ?GraphQLType): boolean %checks { - return type instanceof GraphQLScalarType || type instanceof GraphQLEnumType; +export function isLeafType(type: mixed): boolean %checks { + return isScalarType(type) || isEnumType(type); } -export function assertLeafType(type: ?GraphQLType): GraphQLLeafType { +export function assertLeafType(type: mixed): GraphQLLeafType { invariant( isLeafType(type), `Expected ${String(type)} to be a GraphQL leaf type.`, @@ -163,15 +286,11 @@ export type GraphQLCompositeType = | GraphQLInterfaceType | GraphQLUnionType; -export function isCompositeType(type: ?GraphQLType): boolean %checks { - return ( - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType || - type instanceof GraphQLUnionType - ); +export function isCompositeType(type: mixed): boolean %checks { + return isObjectType(type) || isInterfaceType(type) || isUnionType(type); } -export function assertCompositeType(type: ?GraphQLType): GraphQLCompositeType { +export function assertCompositeType(type: mixed): GraphQLCompositeType { invariant( isCompositeType(type), `Expected ${String(type)} to be a GraphQL composite type.`, @@ -184,13 +303,11 @@ export function assertCompositeType(type: ?GraphQLType): GraphQLCompositeType { */ export type GraphQLAbstractType = GraphQLInterfaceType | GraphQLUnionType; -export function isAbstractType(type: ?GraphQLType): boolean %checks { - return ( - type instanceof GraphQLInterfaceType || type instanceof GraphQLUnionType - ); +export function isAbstractType(type: mixed): boolean %checks { + return isInterfaceType(type) || isUnionType(type); } -export function assertAbstractType(type: ?GraphQLType): GraphQLAbstractType { +export function assertAbstractType(type: mixed): GraphQLAbstractType { invariant( isAbstractType(type), `Expected ${String(type)} to be a GraphQL abstract type.`, @@ -198,6 +315,24 @@ export function assertAbstractType(type: ?GraphQLType): GraphQLAbstractType { return type; } +/** + * These types wrap and modify other types + */ + +export type GraphQLWrappingType = GraphQLList | GraphQLNonNull; + +export function isWrappingType(type: mixed): boolean %checks { + return isListType(type) || isNonNullType(type); +} + +export function assertWrappingType(type: mixed): GraphQLWrappingType { + invariant( + isWrappingType(type), + `Expected ${String(type)} to be a GraphQL wrapping type.`, + ); + return type; +} + /** * These types can all accept null as a value. */ @@ -210,6 +345,18 @@ export type GraphQLNullableType = | GraphQLInputObjectType | GraphQLList; +export function isNullableType(type: mixed): boolean %checks { + return isType(type) && !isNonNullType(type); +} + +export function assertNullableType(type: mixed): GraphQLNullableType { + invariant( + isNullableType(type), + `Expected ${String(type)} to be a GraphQL nullable type.`, + ); + return type; +} + /* eslint-disable no-redeclare */ declare function getNullableType(type: void | null): void; declare function getNullableType(type: T): T; @@ -217,7 +364,7 @@ declare function getNullableType(type: GraphQLNonNull): T; export function getNullableType(type) { /* eslint-enable no-redeclare */ if (type) { - return type instanceof GraphQLNonNull ? type.ofType : type; + return isNonNullType(type) ? type.ofType : type; } } @@ -232,18 +379,18 @@ export type GraphQLNamedType = | GraphQLEnumType | GraphQLInputObjectType; -export function isNamedType(type: ?GraphQLType): boolean %checks { +export function isNamedType(type: mixed): boolean %checks { return ( - type instanceof GraphQLScalarType || - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType || - type instanceof GraphQLUnionType || - type instanceof GraphQLEnumType || - type instanceof GraphQLInputObjectType + isScalarType(type) || + isObjectType(type) || + isInterfaceType(type) || + isUnionType(type) || + isEnumType(type) || + isInputObjectType(type) ); } -export function assertNamedType(type: ?GraphQLType): GraphQLNamedType { +export function assertNamedType(type: mixed): GraphQLNamedType { invariant( isNamedType(type), `Expected ${String(type)} to be a GraphQL named type.`, @@ -257,14 +404,11 @@ declare function getNamedType(type: GraphQLType): GraphQLNamedType; export function getNamedType(type) { /* eslint-enable no-redeclare */ if (type) { - let unmodifiedType = type; - while ( - unmodifiedType instanceof GraphQLList || - unmodifiedType instanceof GraphQLNonNull - ) { - unmodifiedType = unmodifiedType.ofType; + let unwrappedType = type; + while (isWrappingType(unwrappedType)) { + unwrappedType = unwrappedType.ofType; } - return unmodifiedType; + return unwrappedType; } } @@ -821,7 +965,7 @@ function defineTypes( const includedTypeNames = Object.create(null); types.forEach(objType => { invariant( - objType instanceof GraphQLObjectType, + isObjectType(objType), `${unionType.name} may only contain Object types, it cannot contain: ` + `${String(objType)}.`, ); diff --git a/src/type/directives.js b/src/type/directives.js index 944ac479ec..885087808d 100644 --- a/src/type/directives.js +++ b/src/type/directives.js @@ -15,6 +15,7 @@ import type { GraphQLArgument, } from './definition'; import { GraphQLString, GraphQLBoolean } from './scalars'; +import instanceOf from '../jsutils/instanceOf'; import invariant from '../jsutils/invariant'; import { assertValidName } from '../utilities/assertValidName'; import type { DirectiveDefinitionNode } from '../language/ast'; @@ -23,6 +24,17 @@ import { type DirectiveLocationEnum, } from '../language/directiveLocation'; +/** + * Test if the given value is a GraphQL directive. + */ +declare function isDirective( + directive: mixed, +): boolean %checks(directive instanceof GraphQLDirective); +// eslint-disable-next-line no-redeclare +export function isDirective(directive) { + return instanceOf(directive, GraphQLDirective); +} + /** * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. diff --git a/src/type/index.js b/src/type/index.js index caf6904fd9..86686be003 100644 --- a/src/type/index.js +++ b/src/type/index.js @@ -7,25 +7,49 @@ * @flow */ -// GraphQL Schema definition -export { GraphQLSchema } from './schema'; +export { + // Predicate + isSchema, + // GraphQL Schema definition + GraphQLSchema, +} from './schema'; export { // Predicates isType, + isScalarType, + isObjectType, + isInterfaceType, + isUnionType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, isInputType, isOutputType, isLeafType, isCompositeType, isAbstractType, + isWrappingType, + isNullableType, isNamedType, // Assertions assertType, + assertScalarType, + assertObjectType, + assertInterfaceType, + assertUnionType, + assertEnumType, + assertInputObjectType, + assertListType, + assertNonNullType, assertInputType, assertOutputType, assertLeafType, assertCompositeType, assertAbstractType, + assertWrappingType, + assertNullableType, assertNamedType, // Un-modifiers getNullableType, @@ -46,6 +70,8 @@ export { } from './wrappers'; export { + // Predicate + isDirective, // Directives Definition GraphQLDirective, // Built-in Directives defined by the Spec @@ -96,6 +122,7 @@ export type { GraphQLLeafType, GraphQLCompositeType, GraphQLAbstractType, + GraphQLWrappingType, GraphQLNullableType, GraphQLNamedType, Thunk, diff --git a/src/type/introspection.js b/src/type/introspection.js index 3d1ac05d82..0340c182af 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -11,12 +11,16 @@ import isInvalid from '../jsutils/isInvalid'; import { astFromValue } from '../utilities/astFromValue'; import { print } from '../language/printer'; import { - GraphQLScalarType, GraphQLObjectType, - GraphQLInterfaceType, - GraphQLUnionType, GraphQLEnumType, - GraphQLInputObjectType, + isScalarType, + isObjectType, + isInterfaceType, + isUnionType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, isAbstractType, isNamedType, } from './definition'; @@ -212,21 +216,21 @@ export const __Type = new GraphQLObjectType({ kind: { type: GraphQLNonNull(__TypeKind), resolve(type) { - if (type instanceof GraphQLScalarType) { + if (isScalarType(type)) { return TypeKind.SCALAR; - } else if (type instanceof GraphQLObjectType) { + } else if (isObjectType(type)) { return TypeKind.OBJECT; - } else if (type instanceof GraphQLInterfaceType) { + } else if (isInterfaceType(type)) { return TypeKind.INTERFACE; - } else if (type instanceof GraphQLUnionType) { + } else if (isUnionType(type)) { return TypeKind.UNION; - } else if (type instanceof GraphQLEnumType) { + } else if (isEnumType(type)) { return TypeKind.ENUM; - } else if (type instanceof GraphQLInputObjectType) { + } else if (isInputObjectType(type)) { return TypeKind.INPUT_OBJECT; - } else if (type instanceof GraphQLList) { + } else if (isListType(type)) { return TypeKind.LIST; - } else if (type instanceof GraphQLNonNull) { + } else if (isNonNullType(type)) { return TypeKind.NON_NULL; } throw new Error('Unknown kind of type: ' + type); @@ -240,10 +244,7 @@ export const __Type = new GraphQLObjectType({ includeDeprecated: { type: GraphQLBoolean, defaultValue: false }, }, resolve(type, { includeDeprecated }) { - if ( - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType - ) { + if (isObjectType(type) || isInterfaceType(type)) { const fieldMap = type.getFields(); let fields = Object.keys(fieldMap).map( fieldName => fieldMap[fieldName], @@ -259,7 +260,7 @@ export const __Type = new GraphQLObjectType({ interfaces: { type: GraphQLList(GraphQLNonNull(__Type)), resolve(type) { - if (type instanceof GraphQLObjectType) { + if (isObjectType(type)) { return type.getInterfaces(); } }, @@ -278,7 +279,7 @@ export const __Type = new GraphQLObjectType({ includeDeprecated: { type: GraphQLBoolean, defaultValue: false }, }, resolve(type, { includeDeprecated }) { - if (type instanceof GraphQLEnumType) { + if (isEnumType(type)) { let values = type.getValues(); if (!includeDeprecated) { values = values.filter(value => !value.deprecationReason); @@ -290,7 +291,7 @@ export const __Type = new GraphQLObjectType({ inputFields: { type: GraphQLList(GraphQLNonNull(__InputValue)), resolve(type) { - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { const fieldMap = type.getFields(); return Object.keys(fieldMap).map(fieldName => fieldMap[fieldName]); } diff --git a/src/type/schema.js b/src/type/schema.js index f8730222f4..d5526443a0 100644 --- a/src/type/schema.js +++ b/src/type/schema.js @@ -8,25 +8,38 @@ */ import { - GraphQLObjectType, - GraphQLInputObjectType, - GraphQLInterfaceType, - GraphQLUnionType, + isObjectType, + isInterfaceType, + isUnionType, + isInputObjectType, + isWrappingType, } from './definition'; -import { GraphQLList, GraphQLNonNull } from './wrappers'; import type { GraphQLType, GraphQLNamedType, GraphQLAbstractType, + GraphQLObjectType, + GraphQLInterfaceType, } from './definition'; import type { SchemaDefinitionNode } from '../language/ast'; import { GraphQLDirective, specifiedDirectives } from './directives'; import type { GraphQLError } from '../error/GraphQLError'; import { __Schema } from './introspection'; import find from '../jsutils/find'; +import instanceOf from '../jsutils/instanceOf'; import invariant from '../jsutils/invariant'; import type { ObjMap } from '../jsutils/ObjMap'; +/** + * Test if the given value is a GraphQL schema. + */ +declare function isSchema(schema: mixed): boolean %checks(schema instanceof + GraphQLSchema); +// eslint-disable-next-line no-redeclare +export function isSchema(schema) { + return instanceOf(schema, GraphQLSchema); +} + /** * Schema Definition * @@ -117,7 +130,7 @@ export class GraphQLSchema { this._implementations = Object.create(null); Object.keys(this._typeMap).forEach(typeName => { const type = this._typeMap[typeName]; - if (type instanceof GraphQLObjectType) { + if (isObjectType(type)) { type.getInterfaces().forEach(iface => { const impls = this._implementations[iface.name]; if (impls) { @@ -153,11 +166,10 @@ export class GraphQLSchema { getPossibleTypes( abstractType: GraphQLAbstractType, ): $ReadOnlyArray { - if (abstractType instanceof GraphQLUnionType) { + if (isUnionType(abstractType)) { return abstractType.getTypes(); } - invariant(abstractType instanceof GraphQLInterfaceType); - return this._implementations[abstractType.name]; + return this._implementations[(abstractType: GraphQLInterfaceType).name]; } isPossibleType( @@ -211,7 +223,7 @@ function typeMapReducer(map: TypeMap, type: ?GraphQLType): TypeMap { if (!type) { return map; } - if (type instanceof GraphQLList || type instanceof GraphQLNonNull) { + if (isWrappingType(type)) { return typeMapReducer(map, type.ofType); } if (map[type.name]) { @@ -226,18 +238,15 @@ function typeMapReducer(map: TypeMap, type: ?GraphQLType): TypeMap { let reducedMap = map; - if (type instanceof GraphQLUnionType) { + if (isUnionType(type)) { reducedMap = type.getTypes().reduce(typeMapReducer, reducedMap); } - if (type instanceof GraphQLObjectType) { + if (isObjectType(type)) { reducedMap = type.getInterfaces().reduce(typeMapReducer, reducedMap); } - if ( - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType - ) { + if (isObjectType(type) || isInterfaceType(type)) { const fieldMap = type.getFields(); Object.keys(fieldMap).forEach(fieldName => { const field = fieldMap[fieldName]; @@ -250,7 +259,7 @@ function typeMapReducer(map: TypeMap, type: ?GraphQLType): TypeMap { }); } - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { const fieldMap = type.getFields(); Object.keys(fieldMap).forEach(fieldName => { const field = fieldMap[fieldName]; diff --git a/src/type/validate.js b/src/type/validate.js index 1f02dcd39d..ef50d785e3 100644 --- a/src/type/validate.js +++ b/src/type/validate.js @@ -7,11 +7,18 @@ * @flow */ -import { GraphQLInterfaceType, GraphQLObjectType, isType } from './definition'; -import { GraphQLNonNull } from '../type/wrappers'; -import { GraphQLDirective } from './directives'; -import { GraphQLSchema } from './schema'; +import { + isType, + isObjectType, + isInterfaceType, + isNonNullType, +} from './definition'; +import type { GraphQLInterfaceType, GraphQLObjectType } from './definition'; +import { isDirective } from './directives'; +import { isSchema } from './schema'; +import type { GraphQLSchema } from './schema'; import find from '../jsutils/find'; +import invariant from '../jsutils/invariant'; import { isEqualType, isTypeSubTypeOf } from '../utilities/typeComparators'; import type { ASTNode, @@ -33,33 +40,10 @@ export function validateSchema( schema: GraphQLSchema, ): $ReadOnlyArray { // First check to ensure the provided value is in fact a GraphQLSchema. - if (!(schema instanceof GraphQLSchema)) { - if (!schema) { - throw new Error('Must provide schema.'); - } - - // Provide as descriptive an error as possible when attempting to use a - // schema cross-realm. - if (Object.getPrototypeOf(schema).constructor.name === 'GraphQLSchema') { - throw new Error(`Cannot use a GraphQLSchema from another module or realm. - -Ensure that there is only one instance of "graphql" in the node_modules -directory. If different versions of "graphql" are the dependencies of other -relied on modules, use "resolutions" to ensure only one version is installed. - -https://yarnpkg.com/en/docs/selective-version-resolutions - -Duplicate "graphql" modules cannot be used at the same time since different -versions may have different capabilities and behavior. The data from one -version used in the function from another could produce confusing and -spurious results.`); - } else { - throw new Error( - 'Schema must be an instance of GraphQLSchema. ' + - `Received: ${String(schema)}`, - ); - } - } + invariant( + isSchema(schema), + `Expected ${String(schema)} to be a GraphQL schema.`, + ); // If this Schema has already been validated, return the previous results. if (schema.__validationErrors) { @@ -114,7 +98,7 @@ function validateRootTypes(context, schema) { const queryType = schema.getQueryType(); if (!queryType) { context.reportError(`Query root type must be provided.`, schema.astNode); - } else if (!(queryType instanceof GraphQLObjectType)) { + } else if (!isObjectType(queryType)) { context.reportError( `Query root type must be Object type but got: ${String(queryType)}.`, getOperationTypeNode(schema, queryType, 'query'), @@ -122,7 +106,7 @@ function validateRootTypes(context, schema) { } const mutationType = schema.getMutationType(); - if (mutationType && !(mutationType instanceof GraphQLObjectType)) { + if (mutationType && !isObjectType(mutationType)) { context.reportError( 'Mutation root type must be Object type if provided but got: ' + `${String(mutationType)}.`, @@ -131,7 +115,7 @@ function validateRootTypes(context, schema) { } const subscriptionType = schema.getSubscriptionType(); - if (subscriptionType && !(subscriptionType instanceof GraphQLObjectType)) { + if (subscriptionType && !isObjectType(subscriptionType)) { context.reportError( 'Subscription root type must be Object type if provided but got: ' + `${String(subscriptionType)}.`, @@ -160,7 +144,7 @@ function validateDirectives( ): void { const directives = schema.getDirectives(); directives.forEach(directive => { - if (!(directive instanceof GraphQLDirective)) { + if (!isDirective(directive)) { context.reportError( `Expected directive but got: ${String(directive)}.`, directive && directive.astNode, @@ -186,7 +170,7 @@ function validateTypes( } // Ensure objects implement the interfaces they claim to. - if (type instanceof GraphQLObjectType) { + if (isObjectType(type)) { const implementedTypeNames = Object.create(null); type.getInterfaces().forEach(iface => { @@ -209,7 +193,7 @@ function validateObjectImplementsInterface( object: GraphQLObjectType, iface: GraphQLInterfaceType, ): void { - if (!(iface instanceof GraphQLInterfaceType)) { + if (!isInterfaceType(iface)) { context.reportError( `${String(object)} must only implement Interface types, it cannot ` + `implement ${String(iface)}.`, @@ -293,7 +277,7 @@ function validateObjectImplementsInterface( objectField.args.forEach(objectArg => { const argName = objectArg.name; const ifaceArg = find(ifaceField.args, arg => arg.name === argName); - if (!ifaceArg && objectArg.type instanceof GraphQLNonNull) { + if (!ifaceArg && isNonNullType(objectArg.type)) { context.reportError( `${object.name}.${fieldName}(${argName}:) is of required type ` + `"${String(objectArg.type)}" but is not also provided by the ` + diff --git a/src/type/wrappers.js b/src/type/wrappers.js index 69e7af471f..26a2e8ba9b 100644 --- a/src/type/wrappers.js +++ b/src/type/wrappers.js @@ -7,8 +7,7 @@ * @flow */ -import invariant from '../jsutils/invariant'; -import { isType, assertType } from './definition'; +import { assertType, assertNullableType } from './definition'; import type { GraphQLType, GraphQLNullableType } from './definition'; /** @@ -38,8 +37,7 @@ declare class GraphQLList<+T: GraphQLType> { // eslint-disable-next-line no-redeclare export function GraphQLList(ofType) { if (this instanceof GraphQLList) { - assertType(ofType); - this.ofType = ofType; + this.ofType = assertType(ofType); } else { return new GraphQLList(ofType); } @@ -80,13 +78,7 @@ declare class GraphQLNonNull<+T: GraphQLNullableType> { // eslint-disable-next-line no-redeclare export function GraphQLNonNull(ofType) { if (this instanceof GraphQLNonNull) { - invariant( - isType(ofType) && !(ofType instanceof GraphQLNonNull), - `Can only create NonNull of a Nullable GraphQLType but got: ${String( - ofType, - )}.`, - ); - this.ofType = ofType; + this.ofType = assertNullableType(ofType); } else { return new GraphQLNonNull(ofType); } diff --git a/src/utilities/TypeInfo.js b/src/utilities/TypeInfo.js index 26ed8522bb..d052dad0ab 100644 --- a/src/utilities/TypeInfo.js +++ b/src/utilities/TypeInfo.js @@ -9,17 +9,17 @@ import * as Kind from '../language/kinds'; import { + isObjectType, + isInterfaceType, + isEnumType, + isInputObjectType, + isListType, isCompositeType, isInputType, isOutputType, getNullableType, getNamedType, - GraphQLObjectType, - GraphQLInterfaceType, - GraphQLInputObjectType, - GraphQLEnumType, } from '../type/definition'; -import { GraphQLList } from '../type/wrappers'; import type { GraphQLType, GraphQLInputType, @@ -176,13 +176,13 @@ export class TypeInfo { case Kind.LIST: const listType = getNullableType(this.getInputType()); this._inputTypeStack.push( - listType instanceof GraphQLList ? listType.ofType : undefined, + isListType(listType) ? listType.ofType : undefined, ); break; case Kind.OBJECT_FIELD: const objectType = getNamedType(this.getInputType()); let fieldType; - if (objectType instanceof GraphQLInputObjectType) { + if (isInputObjectType(objectType)) { const inputField = objectType.getFields()[node.name.value]; fieldType = inputField ? inputField.type : undefined; } @@ -191,7 +191,7 @@ export class TypeInfo { case Kind.ENUM: const enumType = getNamedType(this.getInputType()); let enumValue; - if (enumType instanceof GraphQLEnumType) { + if (isEnumType(enumType)) { enumValue = enumType.getValue(node.value); } this._enumValue = enumValue; @@ -257,10 +257,7 @@ function getFieldDef( if (name === TypeNameMetaFieldDef.name && isCompositeType(parentType)) { return TypeNameMetaFieldDef; } - if ( - parentType instanceof GraphQLObjectType || - parentType instanceof GraphQLInterfaceType - ) { + if (isObjectType(parentType) || isInterfaceType(parentType)) { return parentType.getFields()[name]; } } diff --git a/src/utilities/__tests__/extendSchema-test.js b/src/utilities/__tests__/extendSchema-test.js index 5c3b355907..aecee24072 100644 --- a/src/utilities/__tests__/extendSchema-test.js +++ b/src/utilities/__tests__/extendSchema-test.js @@ -23,7 +23,8 @@ import { GraphQLEnumType, GraphQLNonNull, GraphQLList, - GraphQLScalarType, + isScalarType, + isNonNullType, } from '../../type'; // Test schema. @@ -910,11 +911,11 @@ describe('extendSchema', () => { expect(args.length).to.equal(2); expect(arg0.name).to.equal('enable'); - expect(arg0.type).to.be.instanceof(GraphQLNonNull); - expect(arg0.type.ofType).to.be.instanceof(GraphQLScalarType); + expect(isNonNullType(arg0.type)).to.equal(true); + expect(isScalarType(arg0.type.ofType)).to.equal(true); expect(arg1.name).to.equal('tag'); - expect(arg1.type).to.be.instanceof(GraphQLScalarType); + expect(isScalarType(arg1.type)).to.equal(true); }); it('does not allow replacing a default directive', () => { diff --git a/src/utilities/astFromValue.js b/src/utilities/astFromValue.js index 88a4d22aed..c06e64fc57 100644 --- a/src/utilities/astFromValue.js +++ b/src/utilities/astFromValue.js @@ -9,7 +9,6 @@ import { forEach, isCollection } from 'iterall'; -import invariant from '../jsutils/invariant'; import isNullish from '../jsutils/isNullish'; import isInvalid from '../jsutils/isInvalid'; import type { @@ -26,11 +25,12 @@ import type { import * as Kind from '../language/kinds'; import type { GraphQLInputType } from '../type/definition'; import { - GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, + isScalarType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; import { GraphQLID } from '../type/scalars'; /** @@ -54,7 +54,7 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { // Ensure flow knows that we treat function params as const. const _value = value; - if (type instanceof GraphQLNonNull) { + if (isNonNullType(type)) { const astValue = astFromValue(_value, type.ofType); if (astValue && astValue.kind === Kind.NULL) { return null; @@ -74,7 +74,7 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { // Convert JavaScript array to GraphQL list. If the GraphQLType is a list, but // the value is not an array, convert the value using the list's item type. - if (type instanceof GraphQLList) { + if (isListType(type)) { const itemType = type.ofType; if (isCollection(_value)) { const valuesNodes = []; @@ -91,7 +91,7 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { // Populate the fields of the input object by creating ASTs from each value // in the JavaScript object according to the fields in the input type. - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { if (_value === null || typeof _value !== 'object') { return null; } @@ -111,49 +111,49 @@ export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { return ({ kind: Kind.OBJECT, fields: fieldNodes }: ObjectValueNode); } - invariant( - type instanceof GraphQLScalarType || type instanceof GraphQLEnumType, - 'Must provide Input Type, cannot use: ' + String(type), - ); + if (isScalarType(type) || isEnumType(type)) { + // Since value is an internally represented value, it must be serialized + // to an externally represented value before converting into an AST. + const serialized = type.serialize(_value); + if (isNullish(serialized)) { + return null; + } - // Since value is an internally represented value, it must be serialized - // to an externally represented value before converting into an AST. - const serialized = type.serialize(_value); - if (isNullish(serialized)) { - return null; - } + // Others serialize based on their corresponding JavaScript scalar types. + if (typeof serialized === 'boolean') { + return ({ kind: Kind.BOOLEAN, value: serialized }: BooleanValueNode); + } - // Others serialize based on their corresponding JavaScript scalar types. - if (typeof serialized === 'boolean') { - return ({ kind: Kind.BOOLEAN, value: serialized }: BooleanValueNode); - } + // JavaScript numbers can be Int or Float values. + if (typeof serialized === 'number') { + const stringNum = String(serialized); + return /^[0-9]+$/.test(stringNum) + ? ({ kind: Kind.INT, value: stringNum }: IntValueNode) + : ({ kind: Kind.FLOAT, value: stringNum }: FloatValueNode); + } - // JavaScript numbers can be Int or Float values. - if (typeof serialized === 'number') { - const stringNum = String(serialized); - return /^[0-9]+$/.test(stringNum) - ? ({ kind: Kind.INT, value: stringNum }: IntValueNode) - : ({ kind: Kind.FLOAT, value: stringNum }: FloatValueNode); - } + if (typeof serialized === 'string') { + // Enum types use Enum literals. + if (isEnumType(type)) { + return ({ kind: Kind.ENUM, value: serialized }: EnumValueNode); + } - if (typeof serialized === 'string') { - // Enum types use Enum literals. - if (type instanceof GraphQLEnumType) { - return ({ kind: Kind.ENUM, value: serialized }: EnumValueNode); - } + // ID types can use Int literals. + if (type === GraphQLID && /^[0-9]+$/.test(serialized)) { + return ({ kind: Kind.INT, value: serialized }: IntValueNode); + } - // ID types can use Int literals. - if (type === GraphQLID && /^[0-9]+$/.test(serialized)) { - return ({ kind: Kind.INT, value: serialized }: IntValueNode); + // Use JSON stringify, which uses the same string encoding as GraphQL, + // then remove the quotes. + return ({ + kind: Kind.STRING, + value: JSON.stringify(serialized).slice(1, -1), + }: StringValueNode); } - // Use JSON stringify, which uses the same string encoding as GraphQL, - // then remove the quotes. - return ({ - kind: Kind.STRING, - value: JSON.stringify(serialized).slice(1, -1), - }: StringValueNode); + throw new TypeError('Cannot convert value to AST: ' + String(serialized)); } - throw new TypeError('Cannot convert value to AST: ' + String(serialized)); + /* istanbul ignore next */ + throw new Error(`Unknown type: ${(type: empty)}.`); } diff --git a/src/utilities/buildASTSchema.js b/src/utilities/buildASTSchema.js index b7b61d2291..7058df8c4c 100644 --- a/src/utilities/buildASTSchema.js +++ b/src/utilities/buildASTSchema.js @@ -7,7 +7,6 @@ * @flow */ -import invariant from '../jsutils/invariant'; import keyMap from '../jsutils/keyMap'; import keyValMap from '../jsutils/keyValMap'; import type { ObjMap } from '../jsutils/ObjMap'; @@ -47,8 +46,11 @@ import { GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + assertObjectType, + assertInterfaceType, assertInputType, assertOutputType, + assertNullableType, } from '../type/definition'; import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; @@ -103,8 +105,7 @@ function buildWrappedType( } if (inputTypeNode.kind === Kind.NON_NULL_TYPE) { const wrappedType = buildWrappedType(innerType, inputTypeNode.type); - invariant(!(wrappedType instanceof GraphQLNonNull), 'No nesting nonnull.'); - return GraphQLNonNull(wrappedType); + return GraphQLNonNull(assertNullableType(wrappedType)); } return innerType; } @@ -308,14 +309,12 @@ export class ASTDefinitionBuilder { buildObjectType(ref: string | NamedTypeNode): GraphQLObjectType { const type = this.buildType(ref); - invariant(type instanceof GraphQLObjectType, 'Expected Object type.'); - return type; + return assertObjectType(type); } buildInterfaceType(ref: string | NamedTypeNode): GraphQLInterfaceType { const type = this.buildType(ref); - invariant(type instanceof GraphQLInterfaceType, 'Expected Interface type.'); - return type; + return assertInterfaceType(type); } _buildWrappedType(typeNode: TypeNode): GraphQLType { diff --git a/src/utilities/buildClientSchema.js b/src/utilities/buildClientSchema.js index a80262f00d..cc883b3a88 100644 --- a/src/utilities/buildClientSchema.js +++ b/src/utilities/buildClientSchema.js @@ -25,6 +25,9 @@ import { GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + assertNullableType, + assertObjectType, + assertInterfaceType, } from '../type/definition'; import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; @@ -117,11 +120,7 @@ export function buildClientSchema( throw new Error('Decorated type deeper than introspection query.'); } const nullableType = getType(nullableRef); - invariant( - !(nullableType instanceof GraphQLNonNull), - 'No nesting nonnull.', - ); - return GraphQLNonNull(nullableType); + return GraphQLNonNull(assertNullableType(nullableType)); } if (!typeRef.name) { throw new Error('Unknown type reference: ' + JSON.stringify(typeRef)); @@ -170,22 +169,14 @@ export function buildClientSchema( typeRef: IntrospectionNamedTypeRef, ): GraphQLObjectType { const type = getType(typeRef); - invariant( - type instanceof GraphQLObjectType, - 'Introspection must provide object type for possibleTypes.', - ); - return type; + return assertObjectType(type); } function getInterfaceType( typeRef: IntrospectionTypeRef, ): GraphQLInterfaceType { const type = getType(typeRef); - invariant( - type instanceof GraphQLInterfaceType, - 'Introspection must provide interface type for interfaces.', - ); - return type; + return assertInterfaceType(type); } // Given a type's introspection result, construct the correct diff --git a/src/utilities/coerceValue.js b/src/utilities/coerceValue.js index 593a2f339c..4467fb0cab 100644 --- a/src/utilities/coerceValue.js +++ b/src/utilities/coerceValue.js @@ -13,11 +13,12 @@ import isNullish from '../jsutils/isNullish'; import { GraphQLError } from '../error'; import type { ASTNode } from '../language/ast'; import { - GraphQLEnumType, - GraphQLInputObjectType, - GraphQLScalarType, + isScalarType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; import type { GraphQLInputType } from '../type/definition'; type CoercedValue = {| @@ -41,7 +42,7 @@ export function coerceValue( path?: Path, ): CoercedValue { // A value must be provided if the type is non-null. - if (type instanceof GraphQLNonNull) { + if (isNonNullType(type)) { if (isNullish(value)) { return ofErrors([ coercionError( @@ -59,7 +60,7 @@ export function coerceValue( return ofValue(null); } - if (type instanceof GraphQLScalarType) { + if (isScalarType(type)) { // Scalars determine if a value is valid via parseValue(), which can // throw to indicate failure. If it throws, maintain a reference to // the original error. @@ -78,7 +79,7 @@ export function coerceValue( } } - if (type instanceof GraphQLEnumType) { + if (isEnumType(type)) { if (typeof value === 'string') { const enumValue = type.getValue(value); if (enumValue) { @@ -90,7 +91,7 @@ export function coerceValue( ]); } - if (type instanceof GraphQLList) { + if (isListType(type)) { const itemType = type.ofType; if (isCollection(value)) { let errors; @@ -115,7 +116,7 @@ export function coerceValue( return coercedItem.errors ? coercedItem : ofValue([coercedItem.value]); } - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { if (typeof value !== 'object') { return ofErrors([ coercionError(`Expected object type ${type.name}`, blameNode, path), @@ -133,7 +134,7 @@ export function coerceValue( if (isInvalid(fieldValue)) { if (!isInvalid(field.defaultValue)) { coercedValue[fieldName] = field.defaultValue; - } else if (field.type instanceof GraphQLNonNull) { + } else if (isNonNullType(field.type)) { errors = add( errors, coercionError( @@ -178,7 +179,8 @@ export function coerceValue( return errors ? ofErrors(errors) : ofValue(coercedValue); } - throw new Error(`Unexpected type ${String((type: empty))}`); + /* istanbul ignore next */ + throw new Error(`Unexpected type: ${(type: empty)}.`); } function ofValue(value) { diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.js index 729774286f..36e7956458 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.js @@ -11,9 +11,14 @@ import invariant from '../jsutils/invariant'; import keyMap from '../jsutils/keyMap'; import { ASTDefinitionBuilder } from './buildASTSchema'; import { GraphQLError } from '../error/GraphQLError'; -import { GraphQLSchema } from '../type/schema'; +import { isSchema, GraphQLSchema } from '../type/schema'; import { + isObjectType, + isInterfaceType, + isUnionType, + isListType, + isNonNullType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, @@ -71,10 +76,7 @@ export function extendSchema( documentAST: DocumentNode, options?: Options, ): GraphQLSchema { - invariant( - schema instanceof GraphQLSchema, - 'Must provide valid GraphQLSchema', - ); + invariant(isSchema(schema), 'Must provide valid GraphQLSchema'); invariant( documentAST && documentAST.kind === Kind.DOCUMENT, @@ -122,7 +124,7 @@ export function extendSchema( [def], ); } - if (!(existingType instanceof GraphQLObjectType)) { + if (!isObjectType(existingType)) { throw new GraphQLError( `Cannot extend non-object type "${extendedTypeName}".`, [def], @@ -248,13 +250,13 @@ export function extendSchema( // Given a type's introspection result, construct the correct // GraphQLType instance. function extendType(type: GraphQLNamedType): GraphQLNamedType { - if (type instanceof GraphQLObjectType) { + if (isObjectType(type)) { return extendObjectType(type); } - if (type instanceof GraphQLInterfaceType) { + if (isInterfaceType(type)) { return extendInterfaceType(type); } - if (type instanceof GraphQLUnionType) { + if (isUnionType(type)) { return extendUnionType(type); } return type; @@ -363,10 +365,10 @@ export function extendSchema( } function extendFieldType(typeDef: T): T { - if (typeDef instanceof GraphQLList) { + if (isListType(typeDef)) { return (GraphQLList(extendFieldType(typeDef.ofType)): any); } - if (typeDef instanceof GraphQLNonNull) { + if (isNonNullType(typeDef)) { return (GraphQLNonNull(extendFieldType(typeDef.ofType)): any); } return getTypeFromDef(typeDef); diff --git a/src/utilities/findBreakingChanges.js b/src/utilities/findBreakingChanges.js index a46d3d27a0..a33606cad7 100644 --- a/src/utilities/findBreakingChanges.js +++ b/src/utilities/findBreakingChanges.js @@ -8,17 +8,17 @@ */ import { + isScalarType, + isObjectType, + isInterfaceType, + isUnionType, + isEnumType, + isInputObjectType, + isNonNullType, + isListType, isNamedType, - GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, - GraphQLInterfaceType, - GraphQLObjectType, - GraphQLUnionType, } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; - import type { GraphQLNamedType, GraphQLFieldMap, @@ -140,7 +140,7 @@ export function findTypesThatChangedKind( } const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if (!(oldType instanceof newType.constructor)) { + if (oldType.constructor !== newType.constructor) { breakingChanges.push({ type: BreakingChangeType.TYPE_CHANGED_KIND, description: @@ -175,11 +175,9 @@ export function findArgChanges( const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; if ( - !( - oldType instanceof GraphQLObjectType || - oldType instanceof GraphQLInterfaceType - ) || - !(newType instanceof oldType.constructor) + !(isObjectType(oldType) || isInterfaceType(oldType)) || + !(isObjectType(newType) || isInterfaceType(newType)) || + newType.constructor !== oldType.constructor ) { return; } @@ -235,7 +233,7 @@ export function findArgChanges( const oldArgs = oldTypeFields[fieldName].args; const oldArgDef = oldArgs.find(arg => arg.name === newArgDef.name); if (!oldArgDef) { - if (newArgDef.type instanceof GraphQLNonNull) { + if (isNonNullType(newArgDef.type)) { breakingChanges.push({ type: BreakingChangeType.NON_NULL_ARG_ADDED, description: @@ -262,22 +260,22 @@ export function findArgChanges( } function typeKindName(type: GraphQLNamedType): string { - if (type instanceof GraphQLScalarType) { + if (isScalarType(type)) { return 'a Scalar type'; } - if (type instanceof GraphQLObjectType) { + if (isObjectType(type)) { return 'an Object type'; } - if (type instanceof GraphQLInterfaceType) { + if (isInterfaceType(type)) { return 'an Interface type'; } - if (type instanceof GraphQLUnionType) { + if (isUnionType(type)) { return 'a Union type'; } - if (type instanceof GraphQLEnumType) { + if (isEnumType(type)) { return 'an Enum type'; } - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { return 'an Input type'; } throw new TypeError('Unknown type ' + type.constructor.name); @@ -295,11 +293,9 @@ export function findFieldsThatChangedTypeOnObjectOrInterfaceTypes( const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; if ( - !( - oldType instanceof GraphQLObjectType || - oldType instanceof GraphQLInterfaceType - ) || - !(newType instanceof oldType.constructor) + !(isObjectType(oldType) || isInterfaceType(oldType)) || + !(isObjectType(newType) || isInterfaceType(newType)) || + newType.constructor !== oldType.constructor ) { return; } @@ -355,10 +351,7 @@ export function findFieldsThatChangedTypeOnInputObjectTypes( Object.keys(oldTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLInputObjectType) || - !(newType instanceof GraphQLInputObjectType) - ) { + if (!isInputObjectType(oldType) || !isInputObjectType(newType)) { return; } @@ -398,7 +391,7 @@ export function findFieldsThatChangedTypeOnInputObjectTypes( // Check if a field was added to the input object type Object.keys(newTypeFieldsDef).forEach(fieldName => { if (!(fieldName in oldTypeFieldsDef)) { - if (newTypeFieldsDef[fieldName].type instanceof GraphQLNonNull) { + if (isNonNullType(newTypeFieldsDef[fieldName].type)) { breakingChanges.push({ type: BreakingChangeType.NON_NULL_INPUT_FIELD_ADDED, description: @@ -431,25 +424,25 @@ function isChangeSafeForObjectOrInterfaceField( // if they're both named types, see if their names are equivalent (isNamedType(newType) && oldType.name === newType.name) || // moving from nullable to non-null of the same underlying type is safe - (newType instanceof GraphQLNonNull && + (isNonNullType(newType) && isChangeSafeForObjectOrInterfaceField(oldType, newType.ofType)) ); - } else if (oldType instanceof GraphQLList) { + } else if (isListType(oldType)) { return ( // if they're both lists, make sure the underlying types are compatible - (newType instanceof GraphQLList && + (isListType(newType) && isChangeSafeForObjectOrInterfaceField( oldType.ofType, newType.ofType, )) || // moving from nullable to non-null of the same underlying type is safe - (newType instanceof GraphQLNonNull && + (isNonNullType(newType) && isChangeSafeForObjectOrInterfaceField(oldType, newType.ofType)) ); - } else if (oldType instanceof GraphQLNonNull) { + } else if (isNonNullType(oldType)) { // if they're both non-null, make sure the underlying types are compatible return ( - newType instanceof GraphQLNonNull && + isNonNullType(newType) && isChangeSafeForObjectOrInterfaceField(oldType.ofType, newType.ofType) ); } @@ -463,23 +456,23 @@ function isChangeSafeForInputObjectFieldOrFieldArg( if (isNamedType(oldType)) { // if they're both named types, see if their names are equivalent return isNamedType(newType) && oldType.name === newType.name; - } else if (oldType instanceof GraphQLList) { + } else if (isListType(oldType)) { // if they're both lists, make sure the underlying types are compatible return ( - newType instanceof GraphQLList && + isListType(newType) && isChangeSafeForInputObjectFieldOrFieldArg(oldType.ofType, newType.ofType) ); - } else if (oldType instanceof GraphQLNonNull) { + } else if (isNonNullType(oldType)) { return ( // if they're both non-null, make sure the underlying types are // compatible - (newType instanceof GraphQLNonNull && + (isNonNullType(newType) && isChangeSafeForInputObjectFieldOrFieldArg( oldType.ofType, newType.ofType, )) || // moving from non-null to nullable of the same underlying type is safe - (!(newType instanceof GraphQLNonNull) && + (!isNonNullType(newType) && isChangeSafeForInputObjectFieldOrFieldArg(oldType.ofType, newType)) ); } @@ -501,10 +494,7 @@ export function findTypesRemovedFromUnions( Object.keys(oldTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLUnionType) || - !(newType instanceof GraphQLUnionType) - ) { + if (!isUnionType(oldType) || !isUnionType(newType)) { return; } const typeNamesInNewUnion = Object.create(null); @@ -538,10 +528,7 @@ export function findTypesAddedToUnions( Object.keys(newTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLUnionType) || - !(newType instanceof GraphQLUnionType) - ) { + if (!isUnionType(oldType) || !isUnionType(newType)) { return; } const typeNamesInOldUnion = Object.create(null); @@ -574,10 +561,7 @@ export function findValuesRemovedFromEnums( Object.keys(oldTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLEnumType) || - !(newType instanceof GraphQLEnumType) - ) { + if (!isEnumType(oldType) || !isEnumType(newType)) { return; } const valuesInNewEnum = Object.create(null); @@ -611,10 +595,7 @@ export function findValuesAddedToEnums( Object.keys(oldTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLEnumType) || - !(newType instanceof GraphQLEnumType) - ) { + if (!isEnumType(oldType) || !isEnumType(newType)) { return; } @@ -645,10 +626,7 @@ export function findInterfacesRemovedFromObjectTypes( Object.keys(oldTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLObjectType) || - !(newType instanceof GraphQLObjectType) - ) { + if (!isObjectType(oldType) || !isObjectType(newType)) { return; } @@ -679,10 +657,7 @@ export function findInterfacesAddedToObjectTypes( Object.keys(newTypeMap).forEach(typeName => { const oldType = oldTypeMap[typeName]; const newType = newTypeMap[typeName]; - if ( - !(oldType instanceof GraphQLObjectType) || - !(newType instanceof GraphQLObjectType) - ) { + if (!isObjectType(oldType) || !isObjectType(newType)) { return; } diff --git a/src/utilities/isValidLiteralValue.js b/src/utilities/isValidLiteralValue.js index 4b8b02b30c..4d9e15f6b1 100644 --- a/src/utilities/isValidLiteralValue.js +++ b/src/utilities/isValidLiteralValue.js @@ -15,13 +15,13 @@ import type { } from '../language/ast'; import * as Kind from '../language/kinds'; import { - GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, + isScalarType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; import type { GraphQLInputType } from '../type/definition'; -import invariant from '../jsutils/invariant'; import isInvalid from '../jsutils/isInvalid'; import keyMap from '../jsutils/keyMap'; @@ -37,7 +37,7 @@ export function isValidLiteralValue( valueNode: ValueNode, ): Array { // A value must be provided if the type is non-null. - if (type instanceof GraphQLNonNull) { + if (isNonNullType(type)) { if (!valueNode || valueNode.kind === Kind.NULL) { return [`Expected "${String(type)}", found null.`]; } @@ -55,7 +55,7 @@ export function isValidLiteralValue( } // Lists accept a non-list value as a list of one. - if (type instanceof GraphQLList) { + if (isListType(type)) { const itemType = type.ofType; if (valueNode.kind === Kind.LIST) { return (valueNode: ListValueNode).values.reduce((acc, item, index) => { @@ -69,7 +69,7 @@ export function isValidLiteralValue( } // Input objects check each defined field and look for undefined fields. - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { if (valueNode.kind !== Kind.OBJECT) { return [`Expected "${type.name}", found not an object.`]; } @@ -100,7 +100,7 @@ export function isValidLiteralValue( return errors; } - if (type instanceof GraphQLEnumType) { + if (isEnumType(type)) { if (valueNode.kind !== Kind.ENUM || !type.getValue(valueNode.value)) { return [`Expected type "${type.name}", found ${print(valueNode)}.`]; } @@ -108,19 +108,22 @@ export function isValidLiteralValue( return []; } - invariant(type instanceof GraphQLScalarType, 'Must be a scalar type'); - - // Scalars determine if a literal value is valid via parseLiteral(). - try { - const parseResult = type.parseLiteral(valueNode, null); - if (isInvalid(parseResult)) { - return [`Expected type "${type.name}", found ${print(valueNode)}.`]; + if (isScalarType(type)) { + // Scalars determine if a literal value is valid via parseLiteral(). + try { + const parseResult = type.parseLiteral(valueNode, null); + if (isInvalid(parseResult)) { + return [`Expected type "${type.name}", found ${print(valueNode)}.`]; + } + } catch (error) { + const printed = print(valueNode); + const message = error.message; + return [`Expected type "${type.name}", found ${printed}; ${message}`]; } - } catch (error) { - const printed = print(valueNode); - const message = error.message; - return [`Expected type "${type.name}", found ${printed}; ${message}`]; + + return []; } - return []; + /* istanbul ignore next */ + throw new Error(`Unknown type: ${(type: empty)}.`); } diff --git a/src/utilities/schemaPrinter.js b/src/utilities/schemaPrinter.js index bac60d576a..568274e148 100644 --- a/src/utilities/schemaPrinter.js +++ b/src/utilities/schemaPrinter.js @@ -7,19 +7,26 @@ * @flow */ -import invariant from '../jsutils/invariant'; import isNullish from '../jsutils/isNullish'; import isInvalid from '../jsutils/isInvalid'; import { astFromValue } from '../utilities/astFromValue'; import { print } from '../language/printer'; import type { GraphQLSchema } from '../type/schema'; -import type { GraphQLType, GraphQLNamedType } from '../type/definition'; import { + isScalarType, + isObjectType, + isInterfaceType, + isUnionType, + isEnumType, + isInputObjectType, +} from '../type/definition'; +import type { + GraphQLNamedType, GraphQLScalarType, + GraphQLEnumType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, - GraphQLEnumType, GraphQLInputObjectType, } from '../type/definition'; import { GraphQLString, isSpecifiedScalarType } from '../type/scalars'; @@ -28,7 +35,6 @@ import { DEFAULT_DEPRECATION_REASON, isSpecifiedDirective, } from '../type/directives'; - import { isIntrospectionType } from '../type/introspection'; type Options = {| commentDescriptions?: boolean |}; @@ -145,20 +151,22 @@ function isSchemaOfCommonNames(schema: GraphQLSchema): boolean { return true; } -export function printType(type: GraphQLType, options?: Options): string { - if (type instanceof GraphQLScalarType) { +export function printType(type: GraphQLNamedType, options?: Options): string { + if (isScalarType(type)) { return printScalar(type, options); - } else if (type instanceof GraphQLObjectType) { + } else if (isObjectType(type)) { return printObject(type, options); - } else if (type instanceof GraphQLInterfaceType) { + } else if (isInterfaceType(type)) { return printInterface(type, options); - } else if (type instanceof GraphQLUnionType) { + } else if (isUnionType(type)) { return printUnion(type, options); - } else if (type instanceof GraphQLEnumType) { + } else if (isEnumType(type)) { return printEnum(type, options); + } else if (isInputObjectType(type)) { + return printInputObject(type, options); } - invariant(type instanceof GraphQLInputObjectType); - return printInputObject(type, options); + /* istanbul ignore next */ + throw new Error(`Unknown type: ${(type: empty)}.`); } function printScalar(type: GraphQLScalarType, options): string { diff --git a/src/utilities/typeComparators.js b/src/utilities/typeComparators.js index 285f5d02a5..616a04723b 100644 --- a/src/utilities/typeComparators.js +++ b/src/utilities/typeComparators.js @@ -7,8 +7,12 @@ * @flow */ -import { isAbstractType, GraphQLObjectType } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; +import { + isObjectType, + isListType, + isNonNullType, + isAbstractType, +} from '../type/definition'; import type { GraphQLType, GraphQLCompositeType } from '../type/definition'; import type { GraphQLSchema } from '../type/schema'; @@ -22,12 +26,12 @@ export function isEqualType(typeA: GraphQLType, typeB: GraphQLType): boolean { } // If either type is non-null, the other must also be non-null. - if (typeA instanceof GraphQLNonNull && typeB instanceof GraphQLNonNull) { + if (isNonNullType(typeA) && isNonNullType(typeB)) { return isEqualType(typeA.ofType, typeB.ofType); } // If either type is a list, the other must also be a list. - if (typeA instanceof GraphQLList && typeB instanceof GraphQLList) { + if (isListType(typeA) && isListType(typeB)) { return isEqualType(typeA.ofType, typeB.ofType); } @@ -50,23 +54,25 @@ export function isTypeSubTypeOf( } // If superType is non-null, maybeSubType must also be non-null. - if (superType instanceof GraphQLNonNull) { - if (maybeSubType instanceof GraphQLNonNull) { + if (isNonNullType(superType)) { + if (isNonNullType(maybeSubType)) { return isTypeSubTypeOf(schema, maybeSubType.ofType, superType.ofType); } return false; - } else if (maybeSubType instanceof GraphQLNonNull) { + } + if (isNonNullType(maybeSubType)) { // If superType is nullable, maybeSubType may be non-null or nullable. return isTypeSubTypeOf(schema, maybeSubType.ofType, superType); } // If superType type is a list, maybeSubType type must also be a list. - if (superType instanceof GraphQLList) { - if (maybeSubType instanceof GraphQLList) { + if (isListType(superType)) { + if (isListType(maybeSubType)) { return isTypeSubTypeOf(schema, maybeSubType.ofType, superType.ofType); } return false; - } else if (maybeSubType instanceof GraphQLList) { + } + if (isListType(maybeSubType)) { // If superType is not a list, maybeSubType must also be not a list. return false; } @@ -75,7 +81,7 @@ export function isTypeSubTypeOf( // possible object type. if ( isAbstractType(superType) && - maybeSubType instanceof GraphQLObjectType && + isObjectType(maybeSubType) && schema.isPossibleType(superType, maybeSubType) ) { return true; diff --git a/src/utilities/typeFromAST.js b/src/utilities/typeFromAST.js index 9700c80d49..de0a3891fe 100644 --- a/src/utilities/typeFromAST.js +++ b/src/utilities/typeFromAST.js @@ -7,7 +7,6 @@ * @flow */ -import invariant from '../jsutils/invariant'; import * as Kind from '../language/kinds'; import type { NamedTypeNode, @@ -49,8 +48,11 @@ function typeFromASTImpl(schema, typeNode) { innerType = typeFromAST(schema, typeNode.type); return innerType && GraphQLNonNull(innerType); } - invariant(typeNode.kind === Kind.NAMED_TYPE, 'Must be a named type.'); - return schema.getType(typeNode.name.value); + if (typeNode.kind === Kind.NAMED_TYPE) { + return schema.getType(typeNode.name.value); + } + /* istanbul ignore next */ + throw new Error(`Unexpected type kind: ${(typeNode.kind: empty)}.`); } // This will export typeFromAST with the correct type, but currently exposes // ~26 errors: https://gist.github.com/4a29403a99a8186fcb15064d69c5f3ae diff --git a/src/utilities/valueFromAST.js b/src/utilities/valueFromAST.js index e7b5d77ec8..9cf859bd42 100644 --- a/src/utilities/valueFromAST.js +++ b/src/utilities/valueFromAST.js @@ -8,16 +8,16 @@ */ import keyMap from '../jsutils/keyMap'; -import invariant from '../jsutils/invariant'; import isInvalid from '../jsutils/isInvalid'; import type { ObjMap } from '../jsutils/ObjMap'; import * as Kind from '../language/kinds'; import { - GraphQLScalarType, - GraphQLEnumType, - GraphQLInputObjectType, + isScalarType, + isEnumType, + isInputObjectType, + isListType, + isNonNullType, } from '../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../type/wrappers'; import type { GraphQLInputType } from '../type/definition'; import type { ValueNode, @@ -57,7 +57,7 @@ export function valueFromAST( return; } - if (type instanceof GraphQLNonNull) { + if (isNonNullType(type)) { if (valueNode.kind === Kind.NULL) { return; // Invalid: intentionally return no value. } @@ -81,7 +81,7 @@ export function valueFromAST( return variables[variableName]; } - if (type instanceof GraphQLList) { + if (isListType(type)) { const itemType = type.ofType; if (valueNode.kind === Kind.LIST) { const coercedValues = []; @@ -90,7 +90,7 @@ export function valueFromAST( if (isMissingVariable(itemNodes[i], variables)) { // If an array contains a missing variable, it is either coerced to // null or if the item type is non-null, it considered invalid. - if (itemType instanceof GraphQLNonNull) { + if (isNonNullType(itemType)) { return; // Invalid: intentionally return no value. } coercedValues.push(null); @@ -111,7 +111,7 @@ export function valueFromAST( return [coercedValue]; } - if (type instanceof GraphQLInputObjectType) { + if (isInputObjectType(type)) { if (valueNode.kind !== Kind.OBJECT) { return; // Invalid: intentionally return no value. } @@ -129,7 +129,7 @@ export function valueFromAST( if (!fieldNode || isMissingVariable(fieldNode.value, variables)) { if (!isInvalid(field.defaultValue)) { coercedObj[fieldName] = field.defaultValue; - } else if (field.type instanceof GraphQLNonNull) { + } else if (isNonNullType(field.type)) { return; // Invalid: intentionally return no value. } continue; @@ -143,7 +143,7 @@ export function valueFromAST( return coercedObj; } - if (type instanceof GraphQLEnumType) { + if (isEnumType(type)) { if (valueNode.kind !== Kind.ENUM) { return; // Invalid: intentionally return no value. } @@ -154,21 +154,24 @@ export function valueFromAST( return enumValue.value; } - invariant(type instanceof GraphQLScalarType, 'Must be scalar type'); - - // Scalars fulfill parsing a literal value via parseLiteral(). - // Invalid values represent a failure to parse correctly, in which case - // no value is returned. - let result; - try { - result = type.parseLiteral(valueNode, variables); - } catch (_error) { - return; // Invalid: intentionally return no value. - } - if (isInvalid(result)) { - return; // Invalid: intentionally return no value. + if (isScalarType(type)) { + // Scalars fulfill parsing a literal value via parseLiteral(). + // Invalid values represent a failure to parse correctly, in which case + // no value is returned. + let result; + try { + result = type.parseLiteral(valueNode, variables); + } catch (_error) { + return; // Invalid: intentionally return no value. + } + if (isInvalid(result)) { + return; // Invalid: intentionally return no value. + } + return result; } - return result; + + /* istanbul ignore next */ + throw new Error(`Unknown type: ${(type: empty)}.`); } // Returns true if the provided valueNode is a variable which is not defined diff --git a/src/utilities/valueFromASTUntyped.js b/src/utilities/valueFromASTUntyped.js index 000cda029b..7d64ce3fcf 100644 --- a/src/utilities/valueFromASTUntyped.js +++ b/src/utilities/valueFromASTUntyped.js @@ -58,7 +58,7 @@ export function valueFromASTUntyped( return variables && !isInvalid(variables[variableName]) ? variables[variableName] : undefined; - default: - throw new Error('Unexpected value kind: ' + (valueNode.kind: empty)); } + /* istanbul ignore next */ + throw new Error('Unexpected value kind: ' + (valueNode.kind: empty)); } diff --git a/src/validation/rules/DefaultValuesOfCorrectType.js b/src/validation/rules/DefaultValuesOfCorrectType.js index babb02c9a1..544eca4bff 100644 --- a/src/validation/rules/DefaultValuesOfCorrectType.js +++ b/src/validation/rules/DefaultValuesOfCorrectType.js @@ -10,7 +10,7 @@ import type { ValidationContext } from '../index'; import { GraphQLError } from '../../error'; import { print } from '../../language/printer'; -import { GraphQLNonNull } from '../../type/wrappers'; +import { isNonNullType } from '../../type/definition'; import { isValidLiteralValue } from '../../utilities/isValidLiteralValue'; import type { GraphQLType } from '../../type/definition'; @@ -51,7 +51,7 @@ export function DefaultValuesOfCorrectType(context: ValidationContext): any { const name = node.variable.name.value; const defaultValue = node.defaultValue; const type = context.getInputType(); - if (type instanceof GraphQLNonNull && defaultValue) { + if (isNonNullType(type) && defaultValue) { context.reportError( new GraphQLError( defaultForNonNullArgMessage(name, type, type.ofType), diff --git a/src/validation/rules/FieldsOnCorrectType.js b/src/validation/rules/FieldsOnCorrectType.js index 2ca4250965..644af5791d 100644 --- a/src/validation/rules/FieldsOnCorrectType.js +++ b/src/validation/rules/FieldsOnCorrectType.js @@ -15,8 +15,8 @@ import type { FieldNode } from '../../language/ast'; import type { GraphQLSchema } from '../../type/schema'; import type { GraphQLOutputType } from '../../type/definition'; import { - GraphQLObjectType, - GraphQLInterfaceType, + isObjectType, + isInterfaceType, isAbstractType, } from '../../type/definition'; @@ -134,10 +134,7 @@ function getSuggestedFieldNames( type: GraphQLOutputType, fieldName: string, ): Array { - if ( - type instanceof GraphQLObjectType || - type instanceof GraphQLInterfaceType - ) { + if (isObjectType(type) || isInterfaceType(type)) { const possibleFieldNames = Object.keys(type.getFields()); return suggestionList(fieldName, possibleFieldNames); } diff --git a/src/validation/rules/OverlappingFieldsCanBeMerged.js b/src/validation/rules/OverlappingFieldsCanBeMerged.js index 55cfa395e6..04b1745255 100644 --- a/src/validation/rules/OverlappingFieldsCanBeMerged.js +++ b/src/validation/rules/OverlappingFieldsCanBeMerged.js @@ -21,11 +21,12 @@ import * as Kind from '../../language/kinds'; import { print } from '../../language/printer'; import { getNamedType, + isNonNullType, isLeafType, - GraphQLObjectType, - GraphQLInterfaceType, + isObjectType, + isListType, + isInterfaceType, } from '../../type/definition'; -import { GraphQLList, GraphQLNonNull } from '../../type/wrappers'; import type { GraphQLNamedType, GraphQLOutputType, @@ -575,8 +576,8 @@ function findConflict( const areMutuallyExclusive = parentFieldsAreMutuallyExclusive || (parentType1 !== parentType2 && - parentType1 instanceof GraphQLObjectType && - parentType2 instanceof GraphQLObjectType); + isObjectType(parentType1) && + isObjectType(parentType2)); // The return type for each field. const type1 = def1 && def1.type; @@ -665,25 +666,21 @@ function doTypesConflict( type1: GraphQLOutputType, type2: GraphQLOutputType, ): boolean { - if (type1 instanceof GraphQLList) { - return type2 instanceof GraphQLList + if (isListType(type1)) { + return isListType(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true; } - if (type2 instanceof GraphQLList) { - return type1 instanceof GraphQLList - ? doTypesConflict(type1.ofType, type2.ofType) - : true; + if (isListType(type2)) { + return true; } - if (type1 instanceof GraphQLNonNull) { - return type2 instanceof GraphQLNonNull + if (isNonNullType(type1)) { + return isNonNullType(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true; } - if (type2 instanceof GraphQLNonNull) { - return type1 instanceof GraphQLNonNull - ? doTypesConflict(type1.ofType, type2.ofType) - : true; + if (isNonNullType(type2)) { + return true; } if (isLeafType(type1) || isLeafType(type2)) { return type1 !== type2; @@ -752,10 +749,7 @@ function _collectFieldsAndFragmentNames( case Kind.FIELD: const fieldName = selection.name.value; let fieldDef; - if ( - parentType instanceof GraphQLObjectType || - parentType instanceof GraphQLInterfaceType - ) { + if (isObjectType(parentType) || isInterfaceType(parentType)) { fieldDef = parentType.getFields()[fieldName]; } const responseName = selection.alias diff --git a/src/validation/rules/ProvidedNonNullArguments.js b/src/validation/rules/ProvidedNonNullArguments.js index b03cfa29fd..ee9569232c 100644 --- a/src/validation/rules/ProvidedNonNullArguments.js +++ b/src/validation/rules/ProvidedNonNullArguments.js @@ -10,7 +10,7 @@ import type { ValidationContext } from '../index'; import { GraphQLError } from '../../error'; import keyMap from '../../jsutils/keyMap'; -import { GraphQLNonNull } from '../../type/wrappers'; +import { isNonNullType } from '../../type/definition'; import type { GraphQLType } from '../../type/definition'; export function missingFieldArgMessage( @@ -55,7 +55,7 @@ export function ProvidedNonNullArguments(context: ValidationContext): any { const argNodeMap = keyMap(argNodes, arg => arg.name.value); fieldDef.args.forEach(argDef => { const argNode = argNodeMap[argDef.name]; - if (!argNode && argDef.type instanceof GraphQLNonNull) { + if (!argNode && isNonNullType(argDef.type)) { context.reportError( new GraphQLError( missingFieldArgMessage( @@ -83,7 +83,7 @@ export function ProvidedNonNullArguments(context: ValidationContext): any { const argNodeMap = keyMap(argNodes, arg => arg.name.value); directiveDef.args.forEach(argDef => { const argNode = argNodeMap[argDef.name]; - if (!argNode && argDef.type instanceof GraphQLNonNull) { + if (!argNode && isNonNullType(argDef.type)) { context.reportError( new GraphQLError( missingDirectiveArgMessage( diff --git a/src/validation/rules/VariablesInAllowedPosition.js b/src/validation/rules/VariablesInAllowedPosition.js index 10ffe4baac..857ac75672 100644 --- a/src/validation/rules/VariablesInAllowedPosition.js +++ b/src/validation/rules/VariablesInAllowedPosition.js @@ -9,6 +9,7 @@ import type { ValidationContext } from '../index'; import { GraphQLError } from '../../error'; +import { isNonNullType } from '../../type/definition'; import { GraphQLNonNull } from '../../type/wrappers'; import { isTypeSubTypeOf } from '../../utilities/typeComparators'; import { typeFromAST } from '../../utilities/typeFromAST'; @@ -73,7 +74,7 @@ export function VariablesInAllowedPosition(context: ValidationContext): any { // If a variable definition has a default value, it's effectively non-null. function effectiveType(varType, varDef) { - return !varDef.defaultValue || varType instanceof GraphQLNonNull + return !varDef.defaultValue || isNonNullType(varType) ? varType : GraphQLNonNull(varType); }