diff --git a/src/error/GraphQLError.ts b/src/error/GraphQLError.ts index 7fec9608830..2bb3ccb7cd4 100644 --- a/src/error/GraphQLError.ts +++ b/src/error/GraphQLError.ts @@ -29,6 +29,14 @@ export interface GraphQLErrorOptions { extensions?: Maybe; } +const isGraphQLErrorSymbol = Symbol.for('GraphQLError'); + +export function isGraphQLError(error: unknown): error is GraphQLError { + return ( + typeof error === 'object' && error != null && isGraphQLErrorSymbol in error + ); +} + /** * A GraphQLError describes an Error found during the parse, validate, or * execute phases of performing a GraphQL operation. In addition to a message @@ -36,6 +44,7 @@ export interface GraphQLErrorOptions { * GraphQL document and/or execution result that correspond to the Error. */ export class GraphQLError extends Error { + [isGraphQLErrorSymbol]: true = true; /** * An array of `{ line, column }` locations within the source GraphQL document * which correspond to this error. diff --git a/src/execution/subscribe.ts b/src/execution/subscribe.ts index 7a04480bf44..ac601cac454 100644 --- a/src/execution/subscribe.ts +++ b/src/execution/subscribe.ts @@ -3,7 +3,7 @@ import { isAsyncIterable } from '../jsutils/isAsyncIterable'; import type { Maybe } from '../jsutils/Maybe'; import { addPath, pathToArray } from '../jsutils/Path'; -import { GraphQLError } from '../error/GraphQLError'; +import { GraphQLError, isGraphQLError } from '../error/GraphQLError'; import { locatedError } from '../error/locatedError'; import type { DocumentNode } from '../language/ast'; @@ -169,7 +169,7 @@ export async function createSourceEventStream( } catch (error) { // If it GraphQLError, report it as an ExecutionResult, containing only errors and no data. // Otherwise treat the error as a system-class error and re-throw it. - if (error instanceof GraphQLError) { + if (isGraphQLError(error)) { return { errors: [error] }; } throw error; diff --git a/src/language/source.ts b/src/language/source.ts index 15f65fceeec..f45b6afc60d 100644 --- a/src/language/source.ts +++ b/src/language/source.ts @@ -1,12 +1,13 @@ import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; interface Location { line: number; column: number; } +const isSourceSymbol = Symbol.for('Source'); + /** * A representation of source input to GraphQL. The `name` and `locationOffset` parameters are * optional, but they are useful for clients who store GraphQL documents in source files. @@ -15,6 +16,7 @@ interface Location { * The `line` and `column` properties in `locationOffset` are 1-indexed. */ export class Source { + [isSourceSymbol]: true = true; body: string; name: string; locationOffset: Location; @@ -53,5 +55,7 @@ export class Source { * @internal */ export function isSource(source: unknown): source is Source { - return instanceOf(source, Source); + return ( + typeof source === 'object' && source != null && isSourceSymbol in source + ); } diff --git a/src/type/definition.ts b/src/type/definition.ts index 9eea02e8eaf..c8c0ec06baf 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -2,7 +2,6 @@ import { devAssert } from '../jsutils/devAssert'; import { didYouMean } from '../jsutils/didYouMean'; import { identityFunc } from '../jsutils/identityFunc'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; import { isObjectLike } from '../jsutils/isObjectLike'; import { keyMap } from '../jsutils/keyMap'; import { keyValMap } from '../jsutils/keyValMap'; @@ -75,11 +74,17 @@ export function assertType(type: unknown): GraphQLType { return type; } +const isGraphQLScalarTypeSymbol = Symbol.for('GraphQLScalarType'); + /** * There are predicates for each kind of GraphQL type. */ export function isScalarType(type: unknown): type is GraphQLScalarType { - return instanceOf(type, GraphQLScalarType); + return ( + typeof type === 'object' && + type != null && + isGraphQLScalarTypeSymbol in type + ); } export function assertScalarType(type: unknown): GraphQLScalarType { @@ -89,8 +94,14 @@ export function assertScalarType(type: unknown): GraphQLScalarType { return type; } +const isGraphQLObjectTypeSymbol = Symbol.for('GraphQLObjectType'); + export function isObjectType(type: unknown): type is GraphQLObjectType { - return instanceOf(type, GraphQLObjectType); + return ( + typeof type === 'object' && + type != null && + isGraphQLObjectTypeSymbol in type + ); } export function assertObjectType(type: unknown): GraphQLObjectType { @@ -100,8 +111,14 @@ export function assertObjectType(type: unknown): GraphQLObjectType { return type; } +const isGraphQLInterfaceTypeSymbol = Symbol.for('GraphQLInterfaceType'); + export function isInterfaceType(type: unknown): type is GraphQLInterfaceType { - return instanceOf(type, GraphQLInterfaceType); + return ( + typeof type === 'object' && + type != null && + isGraphQLInterfaceTypeSymbol in type + ); } export function assertInterfaceType(type: unknown): GraphQLInterfaceType { @@ -113,8 +130,12 @@ export function assertInterfaceType(type: unknown): GraphQLInterfaceType { return type; } +const isGraphQLUnionTypeSymbol = Symbol.for('GraphQLUnionType'); + export function isUnionType(type: unknown): type is GraphQLUnionType { - return instanceOf(type, GraphQLUnionType); + return ( + typeof type === 'object' && type != null && isGraphQLUnionTypeSymbol in type + ); } export function assertUnionType(type: unknown): GraphQLUnionType { @@ -124,8 +145,12 @@ export function assertUnionType(type: unknown): GraphQLUnionType { return type; } +const isGraphQLEnumTypeSymbol = Symbol.for('GraphQLEnumType'); + export function isEnumType(type: unknown): type is GraphQLEnumType { - return instanceOf(type, GraphQLEnumType); + return ( + typeof type === 'object' && type != null && isGraphQLEnumTypeSymbol in type + ); } export function assertEnumType(type: unknown): GraphQLEnumType { @@ -135,10 +160,16 @@ export function assertEnumType(type: unknown): GraphQLEnumType { return type; } +const isGraphQLInputObjectTypeSymbol = Symbol.for('GraphQLInputObjectType'); + export function isInputObjectType( type: unknown, ): type is GraphQLInputObjectType { - return instanceOf(type, GraphQLInputObjectType); + return ( + typeof type === 'object' && + type != null && + isGraphQLInputObjectTypeSymbol in type + ); } export function assertInputObjectType(type: unknown): GraphQLInputObjectType { @@ -150,6 +181,8 @@ export function assertInputObjectType(type: unknown): GraphQLInputObjectType { return type; } +const isGraphQLListTypeSymbol = Symbol.for('GraphQLListType'); + export function isListType( type: GraphQLInputType, ): type is GraphQLList; @@ -158,7 +191,9 @@ export function isListType( ): type is GraphQLList; export function isListType(type: unknown): type is GraphQLList; export function isListType(type: unknown): type is GraphQLList { - return instanceOf(type, GraphQLList); + return ( + typeof type === 'object' && type != null && isGraphQLListTypeSymbol in type + ); } export function assertListType(type: unknown): GraphQLList { @@ -168,6 +203,8 @@ export function assertListType(type: unknown): GraphQLList { return type; } +const isGraphQLNonNullTypeSymbol = Symbol.for('GraphQLNonNullType'); + export function isNonNullType( type: GraphQLInputType, ): type is GraphQLNonNull; @@ -180,7 +217,11 @@ export function isNonNullType( export function isNonNullType( type: unknown, ): type is GraphQLNonNull { - return instanceOf(type, GraphQLNonNull); + return ( + typeof type === 'object' && + type != null && + isGraphQLNonNullTypeSymbol in type + ); } export function assertNonNullType(type: unknown): GraphQLNonNull { @@ -319,6 +360,7 @@ export function assertAbstractType(type: unknown): GraphQLAbstractType { * ``` */ export class GraphQLList { + readonly [isGraphQLListTypeSymbol]: true = true; readonly ofType: T; constructor(ofType: T) { @@ -365,6 +407,7 @@ export class GraphQLList { * Note: the enforcement of non-nullability occurs within the executor. */ export class GraphQLNonNull { + readonly [isGraphQLNonNullTypeSymbol]: true = true; readonly ofType: T; constructor(ofType: T) { @@ -555,6 +598,7 @@ export interface GraphQLScalarTypeExtensions { * ``` */ export class GraphQLScalarType { + readonly [isGraphQLScalarTypeSymbol]: true = true; name: string; description: Maybe; specifiedByURL: Maybe; @@ -725,6 +769,7 @@ export interface GraphQLObjectTypeExtensions<_TSource = any, _TContext = any> { * ``` */ export class GraphQLObjectType { + readonly [isGraphQLObjectTypeSymbol]: true = true; name: string; description: Maybe; isTypeOf: Maybe>; @@ -1078,6 +1123,7 @@ export interface GraphQLInterfaceTypeExtensions { * ``` */ export class GraphQLInterfaceType { + readonly [isGraphQLInterfaceTypeSymbol]: true = true; name: string; description: Maybe; resolveType: Maybe>; @@ -1207,6 +1253,7 @@ export interface GraphQLUnionTypeExtensions { * ``` */ export class GraphQLUnionType { + readonly [isGraphQLUnionTypeSymbol]: true = true; name: string; description: Maybe; resolveType: Maybe>; @@ -1334,6 +1381,7 @@ export interface GraphQLEnumTypeExtensions { * will be used as its internal value. */ export class GraphQLEnumType /* */ { + readonly [isGraphQLEnumTypeSymbol]: true = true; name: string; description: Maybe; extensions: Readonly; @@ -1574,6 +1622,7 @@ export interface GraphQLInputObjectTypeExtensions { * ``` */ export class GraphQLInputObjectType { + readonly [isGraphQLInputObjectTypeSymbol]: true = true; name: string; description: Maybe; extensions: Readonly; diff --git a/src/type/directives.ts b/src/type/directives.ts index bb3e441a433..aa28a8f7c98 100644 --- a/src/type/directives.ts +++ b/src/type/directives.ts @@ -1,6 +1,5 @@ import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; import { isObjectLike } from '../jsutils/isObjectLike'; import type { Maybe } from '../jsutils/Maybe'; import { toObjMap } from '../jsutils/toObjMap'; @@ -24,7 +23,11 @@ import { GraphQLBoolean, GraphQLString } from './scalars'; * Test if the given value is a GraphQL directive. */ export function isDirective(directive: unknown): directive is GraphQLDirective { - return instanceOf(directive, GraphQLDirective); + return ( + typeof directive === 'object' && + directive != null && + isGraphQLDirectiveSymbol in directive + ); } export function assertDirective(directive: unknown): GraphQLDirective { @@ -49,11 +52,14 @@ export interface GraphQLDirectiveExtensions { [attributeName: string]: unknown; } +const isGraphQLDirectiveSymbol = Symbol.for('GraphQLDirective'); + /** * Directives are used by the GraphQL runtime as a way of modifying execution * behavior. Type system creators will usually not create these directly. */ export class GraphQLDirective { + [isGraphQLDirectiveSymbol]: true = true; name: string; description: Maybe; locations: ReadonlyArray; diff --git a/src/type/schema.ts b/src/type/schema.ts index 7cc576a18f9..dde9d44c68a 100644 --- a/src/type/schema.ts +++ b/src/type/schema.ts @@ -1,6 +1,5 @@ import { devAssert } from '../jsutils/devAssert'; import { inspect } from '../jsutils/inspect'; -import { instanceOf } from '../jsutils/instanceOf'; import { isObjectLike } from '../jsutils/isObjectLike'; import type { Maybe } from '../jsutils/Maybe'; import type { ObjMap } from '../jsutils/ObjMap'; @@ -43,7 +42,9 @@ import { * Test if the given value is a GraphQL schema. */ export function isSchema(schema: unknown): schema is GraphQLSchema { - return instanceOf(schema, GraphQLSchema); + return ( + typeof schema === 'object' && schema != null && isSchemaSymbol in schema + ); } export function assertSchema(schema: unknown): GraphQLSchema { @@ -66,6 +67,8 @@ export interface GraphQLSchemaExtensions { [attributeName: string]: unknown; } +const isSchemaSymbol = Symbol.for('GraphQLSchema'); + /** * Schema Definition * @@ -139,6 +142,7 @@ export class GraphQLSchema { extensions: Readonly; astNode: Maybe; extensionASTNodes: ReadonlyArray; + [isSchemaSymbol]: true = true; // Used as a cache for validateSchema(). __validationErrors: Maybe>; diff --git a/src/utilities/coerceInputValue.ts b/src/utilities/coerceInputValue.ts index 136bee63c9e..3c15162308b 100644 --- a/src/utilities/coerceInputValue.ts +++ b/src/utilities/coerceInputValue.ts @@ -8,7 +8,7 @@ import { addPath, pathToArray } from '../jsutils/Path'; import { printPathArray } from '../jsutils/printPathArray'; import { suggestionList } from '../jsutils/suggestionList'; -import { GraphQLError } from '../error/GraphQLError'; +import { GraphQLError, isGraphQLError } from '../error/GraphQLError'; import type { GraphQLInputType } from '../type/definition'; import { @@ -154,7 +154,7 @@ function coerceInputValueImpl( try { parseResult = type.parseValue(inputValue); } catch (error) { - if (error instanceof GraphQLError) { + if (isGraphQLError(error)) { onError(pathToArray(path), inputValue, error); } else { onError( diff --git a/src/validation/rules/ValuesOfCorrectTypeRule.ts b/src/validation/rules/ValuesOfCorrectTypeRule.ts index 5d81a3833a0..cee21489194 100644 --- a/src/validation/rules/ValuesOfCorrectTypeRule.ts +++ b/src/validation/rules/ValuesOfCorrectTypeRule.ts @@ -3,7 +3,7 @@ import { inspect } from '../../jsutils/inspect'; import { keyMap } from '../../jsutils/keyMap'; import { suggestionList } from '../../jsutils/suggestionList'; -import { GraphQLError } from '../../error/GraphQLError'; +import { GraphQLError, isGraphQLError } from '../../error/GraphQLError'; import type { ValueNode } from '../../language/ast'; import { print } from '../../language/printer'; @@ -138,7 +138,7 @@ function isValidValueNode(context: ValidationContext, node: ValueNode): void { } } catch (error) { const typeStr = inspect(locationType); - if (error instanceof GraphQLError) { + if (isGraphQLError(error)) { context.reportError(error); } else { context.reportError(