From f003533a8a9bad112d8d5b58e80c93a0f3b04df1 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 14:45:47 -0700 Subject: [PATCH 1/9] TS: Fix strict issues in src/validation * Add `"strict": true` to tsconfig.json * Fix all issues that arise within `src/validation` --- src/type/definition.ts | 6 ++ src/validation/ValidationContext.ts | 32 +++++----- src/validation/__tests__/validation-test.ts | 4 +- src/validation/rules/KnownDirectivesRule.ts | 6 +- src/validation/rules/KnownTypeNamesRule.ts | 3 +- src/validation/rules/NoFragmentCyclesRule.ts | 13 ++-- src/validation/rules/NoUnusedFragmentsRule.ts | 8 ++- src/validation/rules/NoUnusedVariablesRule.ts | 3 +- .../rules/OverlappingFieldsCanBeMergedRule.ts | 62 ++++++++++--------- .../rules/PossibleTypeExtensionsRule.ts | 9 +-- .../rules/UniqueDirectivesPerLocationRule.ts | 4 +- .../rules/UniqueInputFieldNamesRule.ts | 12 +++- src/validation/validate.ts | 4 +- 13 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/type/definition.ts b/src/type/definition.ts index 3d38a43d5a..f7a46c31ea 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -457,6 +457,9 @@ export function getNullableType(type: undefined | null): void; export function getNullableType( type: T | GraphQLNonNull, ): T; +export function getNullableType( + type: Maybe, +): GraphQLNullableType | undefined; export function getNullableType( type: Maybe, ): GraphQLNullableType | undefined { @@ -504,6 +507,9 @@ export function getNamedType(type: undefined | null): void; export function getNamedType(type: GraphQLInputType): GraphQLNamedInputType; export function getNamedType(type: GraphQLOutputType): GraphQLNamedOutputType; export function getNamedType(type: GraphQLType): GraphQLNamedType; +export function getNamedType( + type: Maybe, +): GraphQLNamedType | undefined; export function getNamedType( type: Maybe, ): GraphQLNamedType | undefined { diff --git a/src/validation/ValidationContext.ts b/src/validation/ValidationContext.ts index 0479318b2a..93bf43d391 100644 --- a/src/validation/ValidationContext.ts +++ b/src/validation/ValidationContext.ts @@ -44,15 +44,11 @@ interface VariableUsage { export class ASTValidationContext { private _ast: DocumentNode; private _onError: (error: GraphQLError) => void; - private _fragments: Maybe>; - private _fragmentSpreads: Map< - SelectionSetNode, - ReadonlyArray - >; - + private _fragments: ObjMap | undefined; + private _fragmentSpreads: Map>; private _recursivelyReferencedFragments: Map< OperationDefinitionNode, - ReadonlyArray + Array >; constructor(ast: DocumentNode, onError: (error: GraphQLError) => void) { @@ -72,15 +68,19 @@ export class ASTValidationContext { } getFragment(name: string): Maybe { - if (!this._fragments) { - const fragments = (this._fragments = Object.create(null)); + let fragments: ObjMap; + if (this._fragments) { + fragments = this._fragments; + } else { + fragments = Object.create(null); for (const defNode of this.getDocument().definitions) { if (defNode.kind === Kind.FRAGMENT_DEFINITION) { fragments[defNode.name.value] = defNode; } } + this._fragments = fragments; } - return this._fragments[name]; + return fragments[name]; } getFragmentSpreads( @@ -90,11 +90,10 @@ export class ASTValidationContext { if (!spreads) { spreads = []; const setsToVisit: Array = [node]; - while (setsToVisit.length !== 0) { - const set = setsToVisit.pop(); + let set: SelectionSetNode | undefined; + while ((set = setsToVisit.pop())) { for (const selection of set.selections) { if (selection.kind === Kind.FRAGMENT_SPREAD) { - // @ts-expect-error FIXME: TS Conversion spreads.push(selection); } else if (selection.selectionSet) { setsToVisit.push(selection.selectionSet); @@ -114,15 +113,14 @@ export class ASTValidationContext { fragments = []; const collectedNames = Object.create(null); const nodesToVisit: Array = [operation.selectionSet]; - while (nodesToVisit.length !== 0) { - const node = nodesToVisit.pop(); + let node: SelectionSetNode | undefined; + while ((node = nodesToVisit.pop())) { for (const spread of this.getFragmentSpreads(node)) { const fragName = spread.name.value; if (collectedNames[fragName] !== true) { collectedNames[fragName] = true; const fragment = this.getFragment(fragName); if (fragment) { - // @ts-expect-error FIXME: TS Conversion fragments.push(fragment); nodesToVisit.push(fragment.selectionSet); } @@ -189,7 +187,7 @@ export class ValidationContext extends ASTValidationContext { getVariableUsages(node: NodeWithSelectionSet): ReadonlyArray { let usages = this._variableUsages.get(node); if (!usages) { - const newUsages = []; + const newUsages: Array = []; const typeInfo = new TypeInfo(this._schema); visit( node, diff --git a/src/validation/__tests__/validation-test.ts b/src/validation/__tests__/validation-test.ts index a015bb9bd4..42860051fc 100644 --- a/src/validation/__tests__/validation-test.ts +++ b/src/validation/__tests__/validation-test.ts @@ -3,6 +3,7 @@ import { describe, it } from 'mocha'; import { GraphQLError } from '../../error/GraphQLError'; +import type { DirectiveNode } from '../../language/ast'; import { parse } from '../../language/parser'; import { TypeInfo } from '../../utilities/TypeInfo'; @@ -15,6 +16,7 @@ import { testSchema } from './harness'; describe('Validate: Supports full validation', () => { it('rejects invalid documents', () => { + // TODO ts-expect-error (expects a DocumentNode as a second parameter) expect(() => validate(testSchema, null)).to.throw('Must provide document.'); }); @@ -96,7 +98,7 @@ describe('Validate: Supports full validation', () => { function customRule(context: ValidationContext) { return { - Directive(node) { + Directive(node: DirectiveNode) { const directiveDef = context.getDirective(); const error = new GraphQLError( 'Reporting directive: ' + String(directiveDef), diff --git a/src/validation/rules/KnownDirectivesRule.ts b/src/validation/rules/KnownDirectivesRule.ts index 7418e570e1..652bdde186 100644 --- a/src/validation/rules/KnownDirectivesRule.ts +++ b/src/validation/rules/KnownDirectivesRule.ts @@ -71,12 +71,10 @@ function getDirectiveLocationForASTPath( ancestors: ReadonlyArray>, ): DirectiveLocationEnum | undefined { const appliedTo = ancestors[ancestors.length - 1]; - invariant(!Array.isArray(appliedTo)); + invariant('kind' in appliedTo); - // @ts-expect-error FIXME: TS Conversion switch (appliedTo.kind) { case Kind.OPERATION_DEFINITION: - // @ts-expect-error FIXME: TS Conversion return getDirectiveLocationForOperation(appliedTo.operation); case Kind.FIELD: return DirectiveLocation.FIELD; @@ -115,7 +113,7 @@ function getDirectiveLocationForASTPath( return DirectiveLocation.INPUT_OBJECT; case Kind.INPUT_VALUE_DEFINITION: { const parentNode = ancestors[ancestors.length - 3]; - // @ts-expect-error FIXME: TS Conversion + invariant('kind' in parentNode); return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION ? DirectiveLocation.INPUT_FIELD_DEFINITION : DirectiveLocation.ARGUMENT_DEFINITION; diff --git a/src/validation/rules/KnownTypeNamesRule.ts b/src/validation/rules/KnownTypeNamesRule.ts index 8593e9a5a2..61d8981c3b 100644 --- a/src/validation/rules/KnownTypeNamesRule.ts +++ b/src/validation/rules/KnownTypeNamesRule.ts @@ -74,8 +74,7 @@ const standardTypeNames = [...specifiedScalarTypes, ...introspectionTypes].map( function isSDLNode(value: ASTNode | ReadonlyArray): boolean { return ( - !Array.isArray(value) && - // @ts-expect-error FIXME: TS Conversion + 'kind' in value && (isTypeSystemDefinitionNode(value) || isTypeSystemExtensionNode(value)) ); } diff --git a/src/validation/rules/NoFragmentCyclesRule.ts b/src/validation/rules/NoFragmentCyclesRule.ts index 54fba26e43..eb91391731 100644 --- a/src/validation/rules/NoFragmentCyclesRule.ts +++ b/src/validation/rules/NoFragmentCyclesRule.ts @@ -1,7 +1,12 @@ +import type { ObjMap } from '../../jsutils/ObjMap'; + import { GraphQLError } from '../../error/GraphQLError'; +import type { + FragmentDefinitionNode, + FragmentSpreadNode, +} from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; -import type { FragmentDefinitionNode } from '../../language/ast'; import type { ASTValidationContext } from '../ValidationContext'; @@ -10,13 +15,13 @@ export function NoFragmentCyclesRule( ): ASTVisitor { // Tracks already visited fragments to maintain O(N) and to ensure that cycles // are not redundantly reported. - const visitedFrags = Object.create(null); + const visitedFrags: ObjMap = Object.create(null); // Array of AST nodes used to produce meaningful errors - const spreadPath = []; + const spreadPath: Array = []; // Position in the spread path - const spreadPathIndexByName = Object.create(null); + const spreadPathIndexByName: ObjMap = Object.create(null); return { OperationDefinition: () => false, diff --git a/src/validation/rules/NoUnusedFragmentsRule.ts b/src/validation/rules/NoUnusedFragmentsRule.ts index d69bf241cf..393f783d72 100644 --- a/src/validation/rules/NoUnusedFragmentsRule.ts +++ b/src/validation/rules/NoUnusedFragmentsRule.ts @@ -1,5 +1,9 @@ import { GraphQLError } from '../../error/GraphQLError'; +import type { + FragmentDefinitionNode, + OperationDefinitionNode, +} from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; import type { ASTValidationContext } from '../ValidationContext'; @@ -13,8 +17,8 @@ import type { ASTValidationContext } from '../ValidationContext'; export function NoUnusedFragmentsRule( context: ASTValidationContext, ): ASTVisitor { - const operationDefs = []; - const fragmentDefs = []; + const operationDefs: Array = []; + const fragmentDefs: Array = []; return { OperationDefinition(node) { diff --git a/src/validation/rules/NoUnusedVariablesRule.ts b/src/validation/rules/NoUnusedVariablesRule.ts index 70bc81c941..c58c390f0b 100644 --- a/src/validation/rules/NoUnusedVariablesRule.ts +++ b/src/validation/rules/NoUnusedVariablesRule.ts @@ -1,5 +1,6 @@ import { GraphQLError } from '../../error/GraphQLError'; +import type { VariableDefinitionNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; import type { ValidationContext } from '../ValidationContext'; @@ -11,7 +12,7 @@ import type { ValidationContext } from '../ValidationContext'; * are used, either directly or within a spread fragment. */ export function NoUnusedVariablesRule(context: ValidationContext): ASTVisitor { - let variableDefs = []; + let variableDefs: Array = []; return { OperationDefinition: { diff --git a/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts index 2ae5468cbf..28037485d3 100644 --- a/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts +++ b/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts @@ -17,7 +17,6 @@ import { print } from '../../language/printer'; import type { GraphQLNamedType, GraphQLOutputType, - GraphQLCompositeType, GraphQLField, } from '../../type/definition'; import { @@ -97,12 +96,14 @@ type ConflictReason = [string, ConflictReasonMessage]; type ConflictReasonMessage = string | Array; // Tuple defining a field node in a context. type NodeAndDef = [ - GraphQLCompositeType, + Maybe, FieldNode, Maybe>, ]; // Map of array of those. type NodeAndDefCollection = ObjMap>; +type FragmentNames = Array; +type FieldsAndFragmentNames = readonly [NodeAndDefCollection, FragmentNames]; /** * Algorithm: @@ -164,12 +165,12 @@ type NodeAndDefCollection = ObjMap>; // GraphQL Document. function findConflictsWithinSelectionSet( context: ValidationContext, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, parentType: Maybe, selectionSet: SelectionSetNode, ): Array { - const conflicts = []; + const conflicts: Array = []; const [fieldMap, fragmentNames] = getFieldsAndFragmentNames( context, @@ -226,7 +227,7 @@ function findConflictsWithinSelectionSet( function collectConflictsBetweenFieldsAndFragment( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, areMutuallyExclusive: boolean, fieldMap: NodeAndDefCollection, @@ -280,7 +281,7 @@ function collectConflictsBetweenFieldsAndFragment( function collectConflictsBetweenFragments( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, areMutuallyExclusive: boolean, fragmentName1: string, @@ -366,7 +367,7 @@ function collectConflictsBetweenFragments( // between the sub-fields of two overlapping fields. function findConflictsBetweenSubSelectionSets( context: ValidationContext, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, areMutuallyExclusive: boolean, parentType1: Maybe, @@ -374,7 +375,7 @@ function findConflictsBetweenSubSelectionSets( parentType2: Maybe, selectionSet2: SelectionSetNode, ): Array { - const conflicts = []; + const conflicts: Array = []; const [fieldMap1, fragmentNames1] = getFieldsAndFragmentNames( context, @@ -455,7 +456,7 @@ function findConflictsBetweenSubSelectionSets( function collectConflictsWithin( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, fieldMap: NodeAndDefCollection, ): void { @@ -496,7 +497,7 @@ function collectConflictsWithin( function collectConflictsBetween( context: ValidationContext, conflicts: Array, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, parentFieldsAreMutuallyExclusive: boolean, fieldMap1: NodeAndDefCollection, @@ -534,7 +535,7 @@ function collectConflictsBetween( // comparing their sub-fields. function findConflict( context: ValidationContext, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, comparedFragmentPairs: PairSet, parentFieldsAreMutuallyExclusive: boolean, responseName: string, @@ -677,32 +678,33 @@ function doTypesConflict( // referenced via fragment spreads. function getFieldsAndFragmentNames( context: ValidationContext, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, parentType: Maybe, selectionSet: SelectionSetNode, -): [NodeAndDefCollection, Array] { - let cached = cachedFieldsAndFragmentNames.get(selectionSet); - if (!cached) { - const nodeAndDefs = Object.create(null); - const fragmentNames = Object.create(null); - _collectFieldsAndFragmentNames( - context, - parentType, - selectionSet, - nodeAndDefs, - fragmentNames, - ); - cached = [nodeAndDefs, Object.keys(fragmentNames)]; - cachedFieldsAndFragmentNames.set(selectionSet, cached); +): FieldsAndFragmentNames { + const cached = cachedFieldsAndFragmentNames.get(selectionSet); + if (cached) { + return cached; } - return cached; + const nodeAndDefs: NodeAndDefCollection = Object.create(null); + const fragmentNames: ObjMap = Object.create(null); + _collectFieldsAndFragmentNames( + context, + parentType, + selectionSet, + nodeAndDefs, + fragmentNames, + ); + const result = [nodeAndDefs, Object.keys(fragmentNames)] as const; + cachedFieldsAndFragmentNames.set(selectionSet, result); + return result; } // Given a reference to a fragment, return the represented collection of fields // as well as a list of nested fragment names referenced via fragment spreads. function getReferencedFieldsAndFragmentNames( context: ValidationContext, - cachedFieldsAndFragmentNames, + cachedFieldsAndFragmentNames: Map, fragment: FragmentDefinitionNode, ) { // Short-circuit building a type from the node if possible. @@ -724,8 +726,8 @@ function _collectFieldsAndFragmentNames( context: ValidationContext, parentType: Maybe, selectionSet: SelectionSetNode, - nodeAndDefs, - fragmentNames, + nodeAndDefs: NodeAndDefCollection, + fragmentNames: ObjMap, ): void { for (const selection of selectionSet.selections) { switch (selection.kind) { diff --git a/src/validation/rules/PossibleTypeExtensionsRule.ts b/src/validation/rules/PossibleTypeExtensionsRule.ts index ab7db3105b..11c55eff84 100644 --- a/src/validation/rules/PossibleTypeExtensionsRule.ts +++ b/src/validation/rules/PossibleTypeExtensionsRule.ts @@ -1,3 +1,4 @@ +import type { ObjMap } from '../../jsutils/ObjMap'; import { inspect } from '../../jsutils/inspect'; import { invariant } from '../../jsutils/invariant'; import { didYouMean } from '../../jsutils/didYouMean'; @@ -7,7 +8,7 @@ import { GraphQLError } from '../../error/GraphQLError'; import type { KindEnum } from '../../language/kinds'; import type { ASTVisitor } from '../../language/visitor'; -import type { TypeExtensionNode } from '../../language/ast'; +import type { DefinitionNode, TypeExtensionNode } from '../../language/ast'; import { Kind } from '../../language/kinds'; import { isTypeDefinitionNode } from '../../language/predicates'; @@ -32,7 +33,7 @@ export function PossibleTypeExtensionsRule( context: SDLValidationContext, ): ASTVisitor { const schema = context.getSchema(); - const definedTypes = Object.create(null); + const definedTypes: ObjMap = Object.create(null); for (const def of context.getDocument().definitions) { if (isTypeDefinitionNode(def)) { @@ -54,7 +55,7 @@ export function PossibleTypeExtensionsRule( const defNode = definedTypes[typeName]; const existingType = schema?.getType(typeName); - let expectedKind; + let expectedKind: KindEnum | undefined; if (defNode) { expectedKind = defKindToExtKind[defNode.kind]; } else if (existingType) { @@ -89,7 +90,7 @@ export function PossibleTypeExtensionsRule( } } -const defKindToExtKind = { +const defKindToExtKind: ObjMap = { [Kind.SCALAR_TYPE_DEFINITION]: Kind.SCALAR_TYPE_EXTENSION, [Kind.OBJECT_TYPE_DEFINITION]: Kind.OBJECT_TYPE_EXTENSION, [Kind.INTERFACE_TYPE_DEFINITION]: Kind.INTERFACE_TYPE_EXTENSION, diff --git a/src/validation/rules/UniqueDirectivesPerLocationRule.ts b/src/validation/rules/UniqueDirectivesPerLocationRule.ts index 8bdff5de24..85bea14969 100644 --- a/src/validation/rules/UniqueDirectivesPerLocationRule.ts +++ b/src/validation/rules/UniqueDirectivesPerLocationRule.ts @@ -48,8 +48,7 @@ export function UniqueDirectivesPerLocationRule( // them all, just listen for entering any node, and check to see if it // defines any directives. enter(node) { - // @ts-expect-error FIXME: TS Conversion - if (node.directives == null) { + if (!('directives' in node) || !node.directives) { return; } @@ -69,7 +68,6 @@ export function UniqueDirectivesPerLocationRule( seenDirectives = Object.create(null); } - // @ts-expect-error FIXME: TS Conversion for (const directive of node.directives) { const directiveName = directive.name.value; diff --git a/src/validation/rules/UniqueInputFieldNamesRule.ts b/src/validation/rules/UniqueInputFieldNamesRule.ts index 413783e930..cd6b1161c6 100644 --- a/src/validation/rules/UniqueInputFieldNamesRule.ts +++ b/src/validation/rules/UniqueInputFieldNamesRule.ts @@ -1,5 +1,9 @@ +import type { ObjMap } from '../../jsutils/ObjMap'; +import { invariant } from '../../jsutils/invariant'; + import { GraphQLError } from '../../error/GraphQLError'; +import type { NameNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; import type { ASTValidationContext } from '../ValidationContext'; @@ -13,8 +17,8 @@ import type { ASTValidationContext } from '../ValidationContext'; export function UniqueInputFieldNamesRule( context: ASTValidationContext, ): ASTVisitor { - const knownNameStack = []; - let knownNames = Object.create(null); + const knownNameStack: Array> = []; + let knownNames: ObjMap = Object.create(null); return { ObjectValue: { @@ -23,7 +27,9 @@ export function UniqueInputFieldNamesRule( knownNames = Object.create(null); }, leave() { - knownNames = knownNameStack.pop(); + const prevKnownNames = knownNameStack.pop(); + invariant(prevKnownNames); + knownNames = prevKnownNames; }, }, ObjectField(node) { diff --git a/src/validation/validate.ts b/src/validation/validate.ts index 5988115aae..511942155f 100644 --- a/src/validation/validate.ts +++ b/src/validation/validate.ts @@ -45,7 +45,7 @@ export function validate( assertValidSchema(schema); const abortObj = Object.freeze({}); - const errors = []; + const errors: Array = []; const context = new ValidationContext( schema, documentAST, @@ -87,7 +87,7 @@ export function validateSDL( schemaToExtend?: Maybe, rules: ReadonlyArray = specifiedSDLRules, ): ReadonlyArray { - const errors = []; + const errors: Array = []; const context = new SDLValidationContext( documentAST, schemaToExtend, From b82bb600600e3605fcbe9ed29b120f43dc32a68d Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 15:23:18 -0700 Subject: [PATCH 2/9] TS: Fix strict issues in src/utilities --- src/type/schema.ts | 2 +- src/utilities/TypeInfo.ts | 10 ++++++---- src/utilities/__tests__/TypeInfo-test.ts | 14 +++++++------- .../__tests__/buildASTSchema-test.ts | 3 ++- .../__tests__/buildClientSchema-test.ts | 1 + .../__tests__/coerceInputValue-test.ts | 18 ++++++++++-------- src/utilities/__tests__/extendSchema-test.ts | 1 + src/utilities/coerceInputValue.ts | 2 +- src/utilities/concatAST.ts | 6 +++--- src/utilities/extendSchema.ts | 19 +++++++++---------- src/utilities/findBreakingChanges.ts | 6 +++--- src/utilities/getIntrospectionQuery.ts | 2 +- src/utilities/introspectionFromSchema.ts | 3 +-- src/utilities/lexicographicSortSchema.ts | 11 +++++------ src/utilities/separateOperations.ts | 2 +- 15 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/type/schema.ts b/src/type/schema.ts index 46015b0020..84320007f3 100644 --- a/src/type/schema.ts +++ b/src/type/schema.ts @@ -274,7 +274,7 @@ export class GraphQLSchema { return this._typeMap; } - getType(name: string): Maybe { + getType(name: string): GraphQLNamedType | undefined { return this.getTypeMap()[name]; } diff --git a/src/utilities/TypeInfo.ts b/src/utilities/TypeInfo.ts index ac220a7f30..f9ba319ce6 100644 --- a/src/utilities/TypeInfo.ts +++ b/src/utilities/TypeInfo.ts @@ -337,11 +337,12 @@ export function visitWithTypeInfo( visitor: ASTVisitor, ): ASTVisitor { return { - enter(node) { + enter(...args) { + const node = args[0]; typeInfo.enter(node); const fn = getVisitFn(visitor, node.kind, /* isLeaving */ false); if (fn) { - const result = fn.apply(visitor, arguments); + const result = fn.apply(visitor, args); if (result !== undefined) { typeInfo.leave(node); if (isNode(result)) { @@ -351,11 +352,12 @@ export function visitWithTypeInfo( return result; } }, - leave(node) { + leave(...args) { + const node = args[0]; const fn = getVisitFn(visitor, node.kind, /* isLeaving */ true); let result; if (fn) { - result = fn.apply(visitor, arguments); + result = fn.apply(visitor, args); } typeInfo.leave(node); return result; diff --git a/src/utilities/__tests__/TypeInfo-test.ts b/src/utilities/__tests__/TypeInfo-test.ts index 7650032153..57b1d67491 100644 --- a/src/utilities/__tests__/TypeInfo-test.ts +++ b/src/utilities/__tests__/TypeInfo-test.ts @@ -58,7 +58,7 @@ describe('visitWithTypeInfo', () => { `); const typeInfo = new TypeInfo(schema); - const rootTypes = {}; + const rootTypes: any = {}; visit( ast, visitWithTypeInfo(typeInfo, { @@ -80,7 +80,7 @@ describe('visitWithTypeInfo', () => { '{ human(id: 4) { name, pets { ... { name } }, unknown } }', ); - const visitorArgs = []; + const visitorArgs: Array = []; visit(ast, { enter(...args) { visitorArgs.push(['enter', ...args]); @@ -90,7 +90,7 @@ describe('visitWithTypeInfo', () => { }, }); - const wrappedVisitorArgs = []; + const wrappedVisitorArgs: Array = []; const typeInfo = new TypeInfo(testSchema); visit( ast, @@ -108,7 +108,7 @@ describe('visitWithTypeInfo', () => { }); it('maintains type info during visit', () => { - const visited = []; + const visited: Array = []; const typeInfo = new TypeInfo(testSchema); @@ -193,7 +193,7 @@ describe('visitWithTypeInfo', () => { }); it('maintains type info during edit', () => { - const visited = []; + const visited: Array = []; const typeInfo = new TypeInfo(testSchema); const ast = parse('{ human(id: 4) { name, pets }, alien }'); @@ -314,7 +314,7 @@ describe('visitWithTypeInfo', () => { const typeInfo = new TypeInfo(testSchema, complexInputType); - const visited = []; + const visited: Array = []; visit( ast, visitWithTypeInfo(typeInfo, { @@ -363,7 +363,7 @@ describe('visitWithTypeInfo', () => { const operationNode = ast.definitions[0]; invariant(operationNode.kind === 'OperationDefinition'); - const visited = []; + const visited: Array = []; visit( operationNode.selectionSet, visitWithTypeInfo(typeInfo, { diff --git a/src/utilities/__tests__/buildASTSchema-test.ts b/src/utilities/__tests__/buildASTSchema-test.ts index 6503f6a02e..69d09c6705 100644 --- a/src/utilities/__tests__/buildASTSchema-test.ts +++ b/src/utilities/__tests__/buildASTSchema-test.ts @@ -89,7 +89,7 @@ describe('Schema Builder', () => { const source = '{ add(x: 34, y: 55) }'; const rootValue = { - add: ({ x, y }) => x + y, + add: ({ x, y }: { x: number; y: number }) => x + y, }; expect(graphqlSync({ schema, source, rootValue })).to.deep.equal({ data: { add: 89 }, @@ -1095,6 +1095,7 @@ describe('Schema Builder', () => { }); it('Rejects invalid AST', () => { + // TODO ts-expect-error (First parameter expected to be DocumentNode) expect(() => buildASTSchema(null)).to.throw( 'Must provide valid Document AST', ); diff --git a/src/utilities/__tests__/buildClientSchema-test.ts b/src/utilities/__tests__/buildClientSchema-test.ts index 49c4253b53..aa516bf7a1 100644 --- a/src/utilities/__tests__/buildClientSchema-test.ts +++ b/src/utilities/__tests__/buildClientSchema-test.ts @@ -629,6 +629,7 @@ describe('Type System: build schema from introspection', () => { `); it('throws when introspection is missing __schema property', () => { + // TODO ts-expect-error (First parameter expected to be introspection results) expect(() => buildClientSchema(null)).to.throw( 'Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: null.', ); diff --git a/src/utilities/__tests__/coerceInputValue-test.ts b/src/utilities/__tests__/coerceInputValue-test.ts index 79ee43de23..32e127619a 100644 --- a/src/utilities/__tests__/coerceInputValue-test.ts +++ b/src/utilities/__tests__/coerceInputValue-test.ts @@ -15,18 +15,20 @@ import { coerceInputValue } from '../coerceInputValue'; interface CoerceResult { value: unknown; - errors: ReadonlyArray<{ - path: ReadonlyArray; - value: unknown; - error: string; - }>; + errors: ReadonlyArray; +} + +interface CoerceError { + path: ReadonlyArray; + value: unknown; + error: string; } function coerceValue( inputValue: unknown, type: GraphQLInputType, ): CoerceResult { - const errors = []; + const errors: Array = []; const value = coerceInputValue( inputValue, type, @@ -82,7 +84,7 @@ describe('coerceInputValue', () => { describe('for GraphQLScalar', () => { const TestScalar = new GraphQLScalarType({ name: 'TestScalar', - parseValue(input: { error?: string; value?: unknown }) { + parseValue(input: any) { if (input.error != null) { throw new Error(input.error); } @@ -272,7 +274,7 @@ describe('coerceInputValue', () => { }); describe('for GraphQLInputObject with default value', () => { - const makeTestInputObject = (defaultValue) => + const makeTestInputObject = (defaultValue: any) => new GraphQLInputObjectType({ name: 'TestInputObject', fields: { diff --git a/src/utilities/__tests__/extendSchema-test.ts b/src/utilities/__tests__/extendSchema-test.ts index 085752dcc4..d0d51ed505 100644 --- a/src/utilities/__tests__/extendSchema-test.ts +++ b/src/utilities/__tests__/extendSchema-test.ts @@ -1143,6 +1143,7 @@ describe('extendSchema', () => { it('Rejects invalid AST', () => { const schema = new GraphQLSchema({}); + // TODO ts-expect-error (Second argument expects DocumentNode) expect(() => extendSchema(schema, null)).to.throw( 'Must provide valid Document AST', ); diff --git a/src/utilities/coerceInputValue.ts b/src/utilities/coerceInputValue.ts index 1c115f3b99..5515b2c625 100644 --- a/src/utilities/coerceInputValue.ts +++ b/src/utilities/coerceInputValue.ts @@ -95,7 +95,7 @@ function coerceInputValueImpl( return; } - const coercedValue = {}; + const coercedValue: any = {}; const fieldDefs = type.getFields(); for (const field of Object.values(fieldDefs)) { diff --git a/src/utilities/concatAST.ts b/src/utilities/concatAST.ts index f3ce0614f7..07cf7367b0 100644 --- a/src/utilities/concatAST.ts +++ b/src/utilities/concatAST.ts @@ -1,4 +1,4 @@ -import type { DocumentNode } from '../language/ast'; +import type { DocumentNode, DefinitionNode } from '../language/ast'; /** * Provided a collection of ASTs, presumably each from different files, @@ -8,9 +8,9 @@ import type { DocumentNode } from '../language/ast'; export function concatAST( documents: ReadonlyArray, ): DocumentNode { - let definitions = []; + const definitions: Array = []; for (const doc of documents) { - definitions = definitions.concat(doc.definitions); + definitions.push(...doc.definitions); } return { kind: 'Document', definitions }; } diff --git a/src/utilities/extendSchema.ts b/src/utilities/extendSchema.ts index 98eb8a2083..5ad1583597 100644 --- a/src/utilities/extendSchema.ts +++ b/src/utilities/extendSchema.ts @@ -379,7 +379,7 @@ export function extendSchemaImpl( return { ...field, type: replaceType(field.type), - args: mapValue(field.args, extendArg), + args: field.args && mapValue(field.args, extendArg), }; } @@ -393,9 +393,9 @@ export function extendSchemaImpl( function getOperationTypes( nodes: ReadonlyArray, ): { - query: Maybe; - mutation: Maybe; - subscription: Maybe; + query?: Maybe; + mutation?: Maybe; + subscription?: Maybe; } { const opTypes = {}; for (const node of nodes) { @@ -403,14 +403,14 @@ export function extendSchemaImpl( const operationTypesNodes = node.operationTypes ?? []; for (const operationType of operationTypesNodes) { + // Note: While this could make early assertions to get the correctly + // typed values below, that would throw immediately while type system + // validation with validateSchema() will produce more actionable results. + // TODO ts-expect-error opTypes[operationType.operation] = getNamedType(operationType.type); } } - // Note: While this could make early assertions to get the correctly - // typed values below, that would throw immediately while type system - // validation with validateSchema() will produce more actionable results. - // @ts-expect-error return opTypes; } @@ -656,8 +656,7 @@ export function extendSchemaImpl( } const stdTypeMap = keyMap( - // @ts-expect-error FIXME: TS Conversion - specifiedScalarTypes.concat(introspectionTypes), + [...specifiedScalarTypes, ...introspectionTypes], (type) => type.name, ); diff --git a/src/utilities/findBreakingChanges.ts b/src/utilities/findBreakingChanges.ts index 4d9ee699ef..0da4cceaa7 100644 --- a/src/utilities/findBreakingChanges.ts +++ b/src/utilities/findBreakingChanges.ts @@ -561,9 +561,9 @@ function diff( removed: Array; persisted: Array<[T, T]>; } { - const added = []; - const removed = []; - const persisted = []; + const added: Array = []; + const removed: Array = []; + const persisted: Array<[T, T]> = []; const oldMap = keyMap(oldArray, ({ name }) => name); const newMap = keyMap(newArray, ({ name }) => name); diff --git a/src/utilities/getIntrospectionQuery.ts b/src/utilities/getIntrospectionQuery.ts index 0482031495..15caee5ff4 100644 --- a/src/utilities/getIntrospectionQuery.ts +++ b/src/utilities/getIntrospectionQuery.ts @@ -54,7 +54,7 @@ export function getIntrospectionQuery(options?: IntrospectionOptions): string { ? descriptions : ''; - function inputDeprecation(str) { + function inputDeprecation(str: string) { return optionsWithDefault.inputValueDeprecation ? str : ''; } diff --git a/src/utilities/introspectionFromSchema.ts b/src/utilities/introspectionFromSchema.ts index 105f5bc055..22dfc8a944 100644 --- a/src/utilities/introspectionFromSchema.ts +++ b/src/utilities/introspectionFromSchema.ts @@ -36,6 +36,5 @@ export function introspectionFromSchema( const document = parse(getIntrospectionQuery(optionsWithDefaults)); const result = executeSync({ schema, document }); invariant(!result.errors && result.data); - // @ts-expect-error FIXME - return result.data; + return result.data as any; } diff --git a/src/utilities/lexicographicSortSchema.ts b/src/utilities/lexicographicSortSchema.ts index 7b015f20a4..0bb17d1734 100644 --- a/src/utilities/lexicographicSortSchema.ts +++ b/src/utilities/lexicographicSortSchema.ts @@ -68,13 +68,12 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { } function replaceNamedType(type: T): T { - // @ts-expect-error - return typeMap[type.name]; + return typeMap[type.name] as T; } - function replaceMaybeType>( - maybeType: T, - ): T { + function replaceMaybeType( + maybeType: Maybe, + ): Maybe { return maybeType && replaceNamedType(maybeType); } @@ -98,7 +97,7 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema { return sortObjMap(fieldsMap, (field) => ({ ...field, type: replaceType(field.type), - args: sortArgs(field.args), + args: field.args && sortArgs(field.args), })); } diff --git a/src/utilities/separateOperations.ts b/src/utilities/separateOperations.ts index 6f33d4a234..18884059e9 100644 --- a/src/utilities/separateOperations.ts +++ b/src/utilities/separateOperations.ts @@ -85,7 +85,7 @@ function collectTransitiveDependencies( } function collectDependencies(selectionSet: SelectionSetNode): Array { - const dependencies = []; + const dependencies: Array = []; visit(selectionSet, { FragmentSpread(node) { From 6858154fe8e0d0f2e1f695afa534b55a91f0c5ca Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 16:25:38 -0700 Subject: [PATCH 3/9] TS: Fix strict issues in src/type --- src/type/__tests__/definition-test.ts | 22 +++++----- src/type/__tests__/introspection-test.ts | 11 ++++- src/type/__tests__/scalars-test.ts | 2 +- src/type/__tests__/schema-test.ts | 4 +- src/type/__tests__/validation-test.ts | 5 +++ src/type/definition.ts | 12 +++--- src/type/introspection.ts | 14 +++---- src/type/validate.ts | 51 +++++++++++++----------- 8 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/type/__tests__/definition-test.ts b/src/type/__tests__/definition-test.ts index 12ef0aebf4..6011daaef9 100644 --- a/src/type/__tests__/definition-test.ts +++ b/src/type/__tests__/definition-test.ts @@ -83,11 +83,9 @@ describe('Type System: Scalars', () => { }, }); - // @ts-expect-error FIXME: TS Conversion expect(scalar.parseLiteral(parseValue('null'))).to.equal( 'parseValue: null', ); - // @ts-expect-error FIXME: TS Conversion expect(scalar.parseLiteral(parseValue('{ foo: "bar" }'))).to.equal( 'parseValue: { foo: "bar" }', ); @@ -335,6 +333,7 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: { + // TODO ts-expect-error (must not be undefined) f: undefined, }, }); @@ -357,7 +356,7 @@ describe('Type System: Objects', () => { it('rejects an Object type with a field function that returns incorrect type', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Wrong type of return) fields() { return [{ field: ScalarType }]; }, @@ -397,7 +396,7 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: {}, - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Expected interfaces to return array) interfaces() { return {}; }, @@ -411,7 +410,7 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: { - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Expected resolve to be a function) field: { type: ScalarType, resolve: {} }, }, }); @@ -425,7 +424,7 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: { - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Expected resolve to be a function) field: { type: ScalarType, resolve: 0 }, }, }); @@ -500,7 +499,7 @@ describe('Type System: Interfaces', () => { const objType = new GraphQLInterfaceType({ name: 'AnotherInterface', fields: {}, - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Expected Array return) interfaces() { return {}; }, @@ -698,6 +697,7 @@ describe('Type System: Enums', () => { () => new GraphQLEnumType({ name: 'SomeEnum', + // TODO ts-expect-error (must not be null) values: { FOO: null }, }), ).to.throw( @@ -796,7 +796,7 @@ describe('Type System: Input Objects', () => { const inputObjType = new GraphQLInputObjectType({ name: 'SomeInputObject', fields: { - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Input fields cannot have resolvers) f: { type: ScalarType, resolve: dummyFunc }, }, }); @@ -809,7 +809,7 @@ describe('Type System: Input Objects', () => { const inputObjType = new GraphQLInputObjectType({ name: 'SomeInputObject', fields: { - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Input fields cannot have resolvers) f: { type: ScalarType, resolve: {} }, }, }); @@ -843,7 +843,9 @@ describe('Type System: List', () => { expectList(String).to.throw( 'Expected [function String] to be a GraphQL type.', ); + // TODO ts-expect-error (must provide type) expectList(null).to.throw('Expected null to be a GraphQL type.'); + // TODO ts-expect-error (must provide type) expectList(undefined).to.throw('Expected undefined to be a GraphQL type.'); }); }); @@ -874,9 +876,11 @@ describe('Type System: Non-Null', () => { expectNonNull(String).to.throw( 'Expected [function String] to be a GraphQL nullable type.', ); + // TODO ts-expect-error (must provide type) expectNonNull(null).to.throw( 'Expected null to be a GraphQL nullable type.', ); + // TODO ts-expect-error (must provide type) expectNonNull(undefined).to.throw( 'Expected undefined to be a GraphQL nullable type.', ); diff --git a/src/type/__tests__/introspection-test.ts b/src/type/__tests__/introspection-test.ts index 3a01af5a52..0a480c3e71 100644 --- a/src/type/__tests__/introspection-test.ts +++ b/src/type/__tests__/introspection-test.ts @@ -6,6 +6,8 @@ import { getIntrospectionQuery } from '../../utilities/getIntrospectionQuery'; import { graphqlSync } from '../../graphql'; +import type { GraphQLResolveInfo } from '../definition'; + describe('Introspection', () => { it('executes an introspection query', () => { const schema = buildSchema(` @@ -1566,12 +1568,17 @@ describe('Introspection', () => { }); // istanbul ignore next (Called only to fail test) - function fieldResolver(_1, _2, _3, info): never { + function fieldResolver( + _1: any, + _2: any, + _3: any, + info: GraphQLResolveInfo, + ): never { expect.fail(`Called on ${info.parentType.name}::${info.fieldName}`); } // istanbul ignore next (Called only to fail test) - function typeResolver(_1, _2, info): never { + function typeResolver(_1: any, _2: any, info: GraphQLResolveInfo): never { expect.fail(`Called on ${info.parentType.name}::${info.fieldName}`); } diff --git a/src/type/__tests__/scalars-test.ts b/src/type/__tests__/scalars-test.ts index bdfa337585..f30b01eeac 100644 --- a/src/type/__tests__/scalars-test.ts +++ b/src/type/__tests__/scalars-test.ts @@ -507,7 +507,7 @@ describe('Type System: Specified scalar types', () => { serialize({ value: true, valueOf() { - return this.value; + return (this as { value: boolean }).value; }, }), ).to.equal(true); diff --git a/src/type/__tests__/schema-test.ts b/src/type/__tests__/schema-test.ts index 44ace12c11..6b1c4c4061 100644 --- a/src/type/__tests__/schema-test.ts +++ b/src/type/__tests__/schema-test.ts @@ -27,7 +27,7 @@ describe('Type System: Schema', () => { }, }); - const BlogAuthor = new GraphQLObjectType({ + const BlogAuthor: GraphQLObjectType = new GraphQLObjectType({ name: 'Author', fields: () => ({ id: { type: GraphQLString }, @@ -40,7 +40,7 @@ describe('Type System: Schema', () => { }), }); - const BlogArticle = new GraphQLObjectType({ + const BlogArticle: GraphQLObjectType = new GraphQLObjectType({ name: 'Article', fields: { id: { type: GraphQLString }, diff --git a/src/type/__tests__/validation-test.ts b/src/type/__tests__/validation-test.ts index 1e2548c047..61d43a6030 100644 --- a/src/type/__tests__/validation-test.ts +++ b/src/type/__tests__/validation-test.ts @@ -1036,6 +1036,7 @@ describe('Type System: Object fields must have output types', () => { } it('rejects an empty Object field type', () => { + // TODO ts-expect-error (type field must not be undefined) const schema = schemaWithObjectField({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { @@ -1097,6 +1098,7 @@ describe('Type System: Objects can only implement unique interfaces', () => { const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'BadObject', + // TODO ts-expect-error (interfaces must not contain undefined) interfaces: [undefined], fields: { f: { type: GraphQLString } }, }), @@ -1355,6 +1357,7 @@ describe('Type System: Interface fields must have output types', () => { } it('rejects an empty Interface field type', () => { + // TODO ts-expect-error (type field must not be undefined) const schema = schemaWithInterfaceField({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { @@ -1490,6 +1493,7 @@ describe('Type System: Arguments must have input types', () => { } it('rejects an empty field arg type', () => { + // TODO ts-expect-error (type field must not be undefined) const schema = schemaWithArg({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { @@ -1627,6 +1631,7 @@ describe('Type System: Input Object fields must have input types', () => { } it('rejects an empty input field type', () => { + // TODO ts-expect-error (type field must not be undefined) const schema = schemaWithInputField({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { diff --git a/src/type/definition.ts b/src/type/definition.ts index f7a46c31ea..70b3bbbe1b 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -659,7 +659,7 @@ export type GraphQLScalarValueParser = ( export type GraphQLScalarLiteralParser = ( valueNode: ValueNode, - variables: Maybe>, + variables?: Maybe>, ) => Maybe; export interface GraphQLScalarTypeConfig { @@ -758,8 +758,8 @@ export class GraphQLObjectType { this.astNode = config.astNode; this.extensionASTNodes = config.extensionASTNodes ?? []; - this._fields = defineFieldMap.bind(undefined, config); - this._interfaces = defineInterfaces.bind(undefined, config); + this._fields = () => defineFieldMap(config); + this._interfaces = () => defineInterfaces(config); devAssert(typeof config.name === 'string', 'Must provide name.'); devAssert( config.isTypeOf == null || typeof config.isTypeOf === 'function', @@ -810,8 +810,7 @@ export class GraphQLObjectType { function defineInterfaces( config: Readonly< - | GraphQLObjectTypeConfig - | GraphQLInterfaceTypeConfig + GraphQLObjectTypeConfig | GraphQLInterfaceTypeConfig >, ): Array { const interfaces = resolveArrayThunk(config.interfaces ?? []); @@ -834,7 +833,6 @@ function defineFieldMap( `${config.name} fields must be an object with field names as keys or a function which returns such an object.`, ); - // @ts-expect-error FIXME: TS Conversion return mapValue(fieldMap, (fieldConfig, fieldName) => { devAssert( isPlainObj(fieldConfig), @@ -1048,7 +1046,7 @@ export interface GraphQLField< name: string; description: Maybe; type: GraphQLOutputType; - args: Array; + args: ReadonlyArray; resolve?: GraphQLFieldResolver; subscribe?: GraphQLFieldResolver; deprecationReason: Maybe; diff --git a/src/type/introspection.ts b/src/type/introspection.ts index 1a9a655b91..5859aa27ed 100644 --- a/src/type/introspection.ts +++ b/src/type/introspection.ts @@ -234,20 +234,18 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({ }, name: { type: GraphQLString, - // @ts-expect-error FIXME: TS Conversion - resolve: (type) => (type.name !== undefined ? type.name : undefined), + resolve: (type) => ('name' in type ? type.name : undefined), }, description: { type: GraphQLString, resolve: (type) => - // @ts-expect-error FIXME: TS Conversion - type.description !== undefined ? type.description : undefined, + // istanbul ignore next (FIXME: add test case) + 'description' in type ? type.description : undefined, }, specifiedByURL: { type: GraphQLString, resolve: (obj) => - // @ts-expect-error FIXME: TS Conversion - obj.specifiedByURL !== undefined ? obj.specifiedByURL : undefined, + 'specifiedByURL' in obj ? obj.specifiedByURL : undefined, }, fields: { type: new GraphQLList(new GraphQLNonNull(__Field)), @@ -312,9 +310,7 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({ }, ofType: { type: __Type, - resolve: (type) => - // @ts-expect-error FIXME: TS Conversion - type.ofType !== undefined ? type.ofType : undefined, + resolve: (type) => ('ofType' in type ? type.ofType : undefined), }, } as GraphQLFieldConfigMap), }); diff --git a/src/type/validate.ts b/src/type/validate.ts index 65062401c7..77420d8bc5 100644 --- a/src/type/validate.ts +++ b/src/type/validate.ts @@ -9,6 +9,12 @@ import type { NamedTypeNode, DirectiveNode, OperationTypeNode, + ObjectTypeDefinitionNode, + ObjectTypeExtensionNode, + InterfaceTypeDefinitionNode, + InterfaceTypeExtensionNode, + UnionTypeDefinitionNode, + UnionTypeExtensionNode, } from '../language/ast'; import { isValidNameError } from '../utilities/assertValidName'; @@ -21,6 +27,7 @@ import type { GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, + GraphQLInputField, } from './definition'; import { assertSchema } from './schema'; import { isIntrospectionType } from './introspection'; @@ -85,7 +92,7 @@ class SchemaValidationContext { readonly _errors: Array; readonly schema: GraphQLSchema; - constructor(schema) { + constructor(schema: GraphQLSchema) { this._errors = []; this.schema = schema; } @@ -94,7 +101,9 @@ class SchemaValidationContext { message: string, nodes?: ReadonlyArray> | Maybe, ): void { - const _nodes = Array.isArray(nodes) ? nodes.filter(Boolean) : nodes; + const _nodes = Array.isArray(nodes) + ? (nodes.filter(Boolean) as ReadonlyArray) + : (nodes as Maybe); this.addError(new GraphQLError(message, _nodes)); } @@ -117,8 +126,7 @@ function validateRootTypes(context: SchemaValidationContext): void { `Query root type must be Object type, it cannot be ${inspect( queryType, )}.`, - // @ts-expect-error FIXME: TS Conversion - getOperationTypeNode(schema, 'query') ?? queryType.astNode, + getOperationTypeNode(schema, 'query') ?? (queryType as any).astNode, ); } @@ -127,8 +135,7 @@ function validateRootTypes(context: SchemaValidationContext): void { context.reportError( 'Mutation root type must be Object type if provided, it cannot be ' + `${inspect(mutationType)}.`, - // @ts-expect-error FIXME: TS Conversion - getOperationTypeNode(schema, 'mutation') ?? mutationType.astNode, + getOperationTypeNode(schema, 'mutation') ?? (mutationType as any).astNode, ); } @@ -137,8 +144,8 @@ function validateRootTypes(context: SchemaValidationContext): void { context.reportError( 'Subscription root type must be Object type if provided, it cannot be ' + `${inspect(subscriptionType)}.`, - // @ts-expect-error FIXME: TS Conversion - getOperationTypeNode(schema, 'subscription') ?? subscriptionType.astNode, + getOperationTypeNode(schema, 'subscription') ?? + (subscriptionType as any).astNode, ); } } @@ -148,13 +155,9 @@ function getOperationTypeNode( operation: OperationTypeNode, ): Maybe { // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') - return ( - [schema.astNode] - // @ts-expect-error FIXME: TS Conversion - .concat(schema.extensionASTNodes) - .flatMap((schemaNode) => schemaNode?.operationTypes ?? []) - .find((operationNode) => operationNode.operation === operation)?.type - ); + return [schema.astNode, ...schema.extensionASTNodes] + .flatMap((schemaNode) => schemaNode?.operationTypes ?? []) + .find((operationNode) => operationNode.operation === operation)?.type; } function validateDirectives(context: SchemaValidationContext): void { @@ -163,8 +166,7 @@ function validateDirectives(context: SchemaValidationContext): void { if (!isDirective(directive)) { context.reportError( `Expected directive but got: ${inspect(directive)}.`, - // @ts-expect-error FIXME: TS Conversion - directive?.astNode, + (directive as any)?.astNode, ); continue; } @@ -218,8 +220,7 @@ function validateTypes(context: SchemaValidationContext): void { if (!isNamedType(type)) { context.reportError( `Expected GraphQL named type but got: ${inspect(type)}.`, - // @ts-expect-error FIXME: TS Conversion - type.astNode, + (type as any).astNode, ); continue; } @@ -559,7 +560,7 @@ function createInputObjectCircularRefsValidator( const visitedTypes = Object.create(null); // Array of types nodes used to produce meaningful errors - const fieldPath = []; + const fieldPath: Array = []; // Position in the type path const fieldPathIndexByTypeName = Object.create(null); @@ -607,8 +608,12 @@ function getAllImplementsInterfaceNodes( iface: GraphQLInterfaceType, ): ReadonlyArray { const { astNode, extensionASTNodes } = type; - const nodes = - astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; + const nodes: ReadonlyArray< + | ObjectTypeDefinitionNode + | ObjectTypeExtensionNode + | InterfaceTypeDefinitionNode + | InterfaceTypeExtensionNode + > = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') return nodes @@ -621,7 +626,7 @@ function getUnionMemberTypeNodes( typeName: string, ): Maybe> { const { astNode, extensionASTNodes } = union; - const nodes = + const nodes: ReadonlyArray = astNode != null ? [astNode, ...extensionASTNodes] : extensionASTNodes; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') From f41145b6aac077bd0138e7182772bc6d63ffb70b Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 16:45:47 -0700 Subject: [PATCH 4/9] TS: Fix strict issues in src/subscription --- .../__tests__/mapAsyncIterator-test.ts | 18 +++++++++++------- src/subscription/__tests__/simplePubSub.ts | 14 ++++++++++---- src/subscription/__tests__/subscribe-test.ts | 11 +++++++---- src/subscription/mapAsyncIterator.ts | 4 ++-- src/subscription/subscribe.ts | 5 ++--- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/subscription/__tests__/mapAsyncIterator-test.ts b/src/subscription/__tests__/mapAsyncIterator-test.ts index e5e374e256..3378b00891 100644 --- a/src/subscription/__tests__/mapAsyncIterator-test.ts +++ b/src/subscription/__tests__/mapAsyncIterator-test.ts @@ -33,7 +33,9 @@ describe('mapAsyncIterator', () => { next(): Promise> { if (items.length > 0) { - return Promise.resolve({ done: false, value: items.shift() }); + const value = items[0]; + items.shift(); + return Promise.resolve({ done: false, value }); } return Promise.resolve({ done: true, value: undefined }); @@ -105,8 +107,7 @@ describe('mapAsyncIterator', () => { expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); // Early return - // @ts-expect-error FIXME: TS Conversion - expect(await doubles.return()).to.deep.equal({ + expect(await doubles.return('')).to.deep.equal({ value: 'The End', done: true, }); @@ -130,9 +131,11 @@ describe('mapAsyncIterator', () => { return this; }, next() { + const value = items[0]; + items.shift(); return Promise.resolve({ done: items.length === 0, - value: items.shift(), + value, }); }, }; @@ -143,8 +146,7 @@ describe('mapAsyncIterator', () => { expect(await doubles.next()).to.deep.equal({ value: 4, done: false }); // Early return - // @ts-expect-error FIXME: TS Conversion - expect(await doubles.return()).to.deep.equal({ + expect(await doubles.return(0)).to.deep.equal({ value: undefined, done: true, }); @@ -194,9 +196,11 @@ describe('mapAsyncIterator', () => { return this; }, next() { + const value = items[0]; + items.shift(); return Promise.resolve({ done: items.length === 0, - value: items.shift(), + value, }); }, }; diff --git a/src/subscription/__tests__/simplePubSub.ts b/src/subscription/__tests__/simplePubSub.ts index e4d3c5569a..7efdf40e57 100644 --- a/src/subscription/__tests__/simplePubSub.ts +++ b/src/subscription/__tests__/simplePubSub.ts @@ -1,3 +1,5 @@ +import { invariant } from '../../jsutils/invariant'; + /** * Create an AsyncIterator from an EventEmitter. Useful for mocking a * PubSub system for tests. @@ -18,7 +20,7 @@ export class SimplePubSub { getSubscriber(transform: (value: T) => R): AsyncGenerator { const pullQueue: Array<(result: IteratorResult) => void> = []; - const pushQueue = []; + const pushQueue: Array = []; let listening = true; this._subscribers.add(pushValue); @@ -33,13 +35,15 @@ export class SimplePubSub { }; return { - next() { + next(): Promise> { if (!listening) { return Promise.resolve({ value: undefined, done: true }); } if (pushQueue.length > 0) { - return Promise.resolve({ value: pushQueue.shift(), done: false }); + const value = pushQueue[0]; + pushQueue.shift(); + return Promise.resolve({ value, done: false }); } return new Promise((resolve) => pullQueue.push(resolve)); }, @@ -59,7 +63,9 @@ export class SimplePubSub { function pushValue(event: T): void { const value: R = transform(event); if (pullQueue.length > 0) { - pullQueue.shift()({ value, done: false }); + const receiver = pullQueue.shift(); + invariant(receiver); + receiver({ value, done: false }); } else { pushQueue.push(value); } diff --git a/src/subscription/__tests__/subscribe-test.ts b/src/subscription/__tests__/subscribe-test.ts index 85a0a7e574..dcb02e042c 100644 --- a/src/subscription/__tests__/subscribe-test.ts +++ b/src/subscription/__tests__/subscribe-test.ts @@ -42,7 +42,8 @@ const InboxType = new GraphQLObjectType({ }, unread: { type: GraphQLInt, - resolve: (inbox) => inbox.emails.filter((email) => email.unread).length, + resolve: (inbox) => + inbox.emails.filter((email: any) => email.unread).length, }, emails: { type: new GraphQLList(EmailType) }, }, @@ -103,7 +104,7 @@ function createSubscription(pubsub: SimplePubSub) { }, ]; - const data = { + const data: any = { inbox: { emails }, // FIXME: we shouldn't use mapAsyncIterator here since it makes tests way more complex importantEmail: pubsub.getSubscriber((newEmail) => { @@ -122,7 +123,7 @@ function createSubscription(pubsub: SimplePubSub) { } async function expectPromise(promise: Promise) { - let caughtError; + let caughtError: Error; try { await promise; @@ -136,7 +137,7 @@ async function expectPromise(promise: Promise) { toReject() { expect(caughtError).to.be.an.instanceOf(Error); }, - toRejectWith(message) { + toRejectWith(message: string) { expect(caughtError).to.be.an.instanceOf(Error); expect(caughtError).to.have.property('message', message); }, @@ -312,6 +313,7 @@ describe('Subscription Initialization Phase', () => { }), }); + // TODO ts-expect-error (schema must not be null) (await expectPromise(subscribe({ schema: null, document }))).toRejectWith( 'Expected null to be a GraphQL schema.', ); @@ -321,6 +323,7 @@ describe('Subscription Initialization Phase', () => { 'Expected undefined to be a GraphQL schema.', ); + // TODO ts-expect-error (document must not be null) (await expectPromise(subscribe({ schema, document: null }))).toRejectWith( 'Must provide document.', ); diff --git a/src/subscription/mapAsyncIterator.ts b/src/subscription/mapAsyncIterator.ts index 4e9563c752..55fc3b9499 100644 --- a/src/subscription/mapAsyncIterator.ts +++ b/src/subscription/mapAsyncIterator.ts @@ -8,7 +8,6 @@ export function mapAsyncIterator( iterable: AsyncGenerator | AsyncIterable, callback: (value: T) => PromiseOrValue, ): AsyncGenerator { - // $FlowIssue[incompatible-use] const iterator = iterable[Symbol.asyncIterator](); async function mapResult( @@ -39,9 +38,10 @@ export function mapAsyncIterator( return mapResult(await iterator.next()); }, async return(): Promise> { + // If iterator.return() does not exist, then type R must be undefined. return typeof iterator.return === 'function' ? mapResult(await iterator.return()) - : { value: undefined, done: true }; + : { value: undefined as any, done: true }; }, async throw(error?: unknown) { return typeof iterator.throw === 'function' diff --git a/src/subscription/subscribe.ts b/src/subscription/subscribe.ts index 4d194d894c..6b4c6c13bf 100644 --- a/src/subscription/subscribe.ts +++ b/src/subscription/subscribe.ts @@ -92,7 +92,7 @@ export async function subscribe( // the GraphQL specification. The `execute` function provides the // "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the // "ExecuteQuery" algorithm, for which `execute` is also used. - const mapSourceToResponse = (payload) => + const mapSourceToResponse = (payload: unknown) => execute({ schema, document, @@ -161,11 +161,10 @@ export async function createSourceEventStream( ); // Return early errors if execution context failed. - if (Array.isArray(exeContext)) { + if (!('schema' in exeContext)) { return { errors: exeContext }; } - // @ts-expect-error FIXME: TS Conversion const eventStream = await executeSubscription(exeContext); // Assert field returned an event stream, otherwise yield an error. From bf47ef86d0f9706ed790f182529c52e3c4647f35 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 21:51:15 -0700 Subject: [PATCH 5/9] TS: Fix strict issues in src/language --- src/language/__tests__/lexer-test.ts | 3 +- src/language/__tests__/visitor-test.ts | 46 +++--- src/language/ast.ts | 10 +- src/language/location.ts | 3 + src/language/parser.ts | 197 ++++++++----------------- src/language/printLocation.ts | 11 +- src/language/printer.ts | 14 +- src/language/visitor.ts | 83 ++++++++--- 8 files changed, 167 insertions(+), 200 deletions(-) diff --git a/src/language/__tests__/lexer-test.ts b/src/language/__tests__/lexer-test.ts index 32a82ee97f..591b38d456 100644 --- a/src/language/__tests__/lexer-test.ts +++ b/src/language/__tests__/lexer-test.ts @@ -7,6 +7,7 @@ import { inspect } from '../../jsutils/inspect'; import { GraphQLError } from '../../error/GraphQLError'; +import type { Token } from '../ast'; import { Source } from '../source'; import { TokenKind } from '../tokenKind'; import { Lexer, isPunctuatorTokenKind } from '../lexer'; @@ -876,7 +877,7 @@ describe('Lexer', () => { expect(endToken.next).to.equal(null); const tokens = []; - for (let tok = startToken; tok; tok = tok.next) { + for (let tok: Token | null = startToken; tok; tok = tok.next) { if (tokens.length) { // Tokens are double-linked, prev should point to last seen token. expect(tok.prev).to.equal(tokens[tokens.length - 1]); diff --git a/src/language/__tests__/visitor-test.ts b/src/language/__tests__/visitor-test.ts index 7b6820d676..12a3351850 100644 --- a/src/language/__tests__/visitor-test.ts +++ b/src/language/__tests__/visitor-test.ts @@ -3,7 +3,7 @@ import { describe, it } from 'mocha'; import { kitchenSinkQuery } from '../../__testUtils__/kitchenSinkQuery'; -import type { ASTNode } from '../ast'; +import type { ASTNode, SelectionSetNode } from '../ast'; import { isNode } from '../ast'; import { Kind } from '../kinds'; import { parse } from '../parser'; @@ -51,8 +51,7 @@ function checkVisitorFnArgs(ast: any, args: any, isEdited: boolean = false) { } function getValue(node: ASTNode) { - // @ts-expect-error FIXME: TS Conversion - return node.value != null ? node.value : undefined; + return 'value' in node ? node.value : undefined; } describe('Visitor', () => { @@ -62,7 +61,7 @@ describe('Visitor', () => { }); it('validates path argument', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a }', { noLocation: true }); @@ -93,7 +92,7 @@ describe('Visitor', () => { it('validates ancestors argument', () => { const ast = parse('{ a }', { noLocation: true }); - const visitedNodes = []; + const visitedNodes: Array = []; visit(ast, { enter(node, key, parent, _path, ancestors) { @@ -122,7 +121,7 @@ describe('Visitor', () => { it('allows editing a node both on enter and on leave', () => { const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); - let selectionSet; + let selectionSet: SelectionSetNode; const editedAST = visit(ast, { OperationDefinition: { @@ -265,8 +264,7 @@ describe('Visitor', () => { if (node.kind === 'Field' && node.name.value === 'a') { return { kind: 'Field', - // @ts-expect-error FIXME: TS Conversion - selectionSet: [addedField].concat(node.selectionSet), + selectionSet: [addedField, node.selectionSet], }; } if (node === addedField) { @@ -279,7 +277,7 @@ describe('Visitor', () => { }); it('allows skipping a sub-tree', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -317,7 +315,7 @@ describe('Visitor', () => { }); it('allows early exit while visiting', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -352,7 +350,7 @@ describe('Visitor', () => { }); it('allows early exit while leaving', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -389,7 +387,7 @@ describe('Visitor', () => { }); it('allows a named functions visitor API', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }', { noLocation: true }); visit(ast, { @@ -438,7 +436,7 @@ describe('Visitor', () => { }, }); - const visited = []; + const visited: Array = []; visit(customAST, { enter(node) { visited.push(['enter', node.kind, getValue(node)]); @@ -469,7 +467,7 @@ describe('Visitor', () => { noLocation: true, allowLegacyFragmentVariables: true, }); - const visited = []; + const visited: Array = []; visit(ast, { enter(node) { @@ -516,8 +514,8 @@ describe('Visitor', () => { it('visits kitchen sink', () => { const ast = parse(kitchenSinkQuery); - const visited = []; - const argsStack = []; + const visited: Array = []; + const argsStack: Array = []; visit(ast, { enter(node, key, parent) { @@ -895,7 +893,7 @@ describe('Visitor', () => { // Note: nearly identical to the above test of the same test but // using visitInParallel. it('allows skipping a sub-tree', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }'); visit( @@ -938,7 +936,7 @@ describe('Visitor', () => { }); it('allows skipping different sub-trees', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a { x }, b { y} }'); visit( @@ -1014,7 +1012,7 @@ describe('Visitor', () => { // Note: nearly identical to the above test of the same test but // using visitInParallel. it('allows early exit while visiting', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }'); visit( @@ -1054,7 +1052,7 @@ describe('Visitor', () => { }); it('allows early exit from different points', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a { y }, b { x } }'); visit( @@ -1116,7 +1114,7 @@ describe('Visitor', () => { // Note: nearly identical to the above test of the same test but // using visitInParallel. it('allows early exit while leaving', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b { x }, c }'); visit( @@ -1157,7 +1155,7 @@ describe('Visitor', () => { }); it('allows early exit from leaving different points', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a { y }, b { x } }'); visit( @@ -1233,7 +1231,7 @@ describe('Visitor', () => { }); it('allows for editing on enter', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); const editedAST = visit( @@ -1297,7 +1295,7 @@ describe('Visitor', () => { }); it('allows for editing on leave', () => { - const visited = []; + const visited: Array = []; const ast = parse('{ a, b, c { a, b, c } }', { noLocation: true }); const editedAST = visit( diff --git a/src/language/ast.ts b/src/language/ast.ts index 02c7370340..5e72533c28 100644 --- a/src/language/ast.ts +++ b/src/language/ast.ts @@ -76,8 +76,11 @@ export class Token { /** * For non-punctuation tokens, represents the interpreted value of the token. + * + * Note: is undefined for punctuation tokens, but typed as string for + * convenience in the parser. */ - readonly value?: string; + readonly value: string; /** * Tokens exist as nodes in a double-linked-list amongst all tokens @@ -124,9 +127,8 @@ export class Token { /** * @internal */ -export function isNode(maybeNode: unknown): maybeNode is ASTNode { - // eslint-disable-next-line @typescript-eslint/dot-notation - return typeof maybeNode?.['kind'] === 'string'; +export function isNode(maybeNode: any): maybeNode is ASTNode { + return typeof maybeNode?.kind === 'string'; } /** diff --git a/src/language/location.ts b/src/language/location.ts index ee24dbfcc1..36d97f3cca 100644 --- a/src/language/location.ts +++ b/src/language/location.ts @@ -1,3 +1,5 @@ +import { invariant } from '../jsutils/invariant'; + import type { Source } from './source'; const LineRegExp = /\r\n|[\n\r]/g; @@ -19,6 +21,7 @@ export function getLocation(source: Source, position: number): SourceLocation { let line = 1; for (const match of source.body.matchAll(LineRegExp)) { + invariant(typeof match.index === 'number'); if (match.index >= position) { break; } diff --git a/src/language/parser.ts b/src/language/parser.ts index 0477bf9d49..660fc906c1 100644 --- a/src/language/parser.ts +++ b/src/language/parser.ts @@ -23,7 +23,12 @@ import type { FragmentDefinitionNode, ValueNode, ConstValueNode, + IntValueNode, + FloatValueNode, StringValueNode, + BooleanValueNode, + NullValueNode, + EnumValueNode, ListValueNode, ConstListValueNode, ObjectValueNode, @@ -34,6 +39,8 @@ import type { ConstDirectiveNode, TypeNode, NamedTypeNode, + ListTypeNode, + NonNullTypeNode, TypeSystemDefinitionNode, SchemaDefinitionNode, OperationTypeDefinitionNode, @@ -187,9 +194,7 @@ export class Parser { */ parseName(): NameNode { const token = this.expectToken(TokenKind.NAME); - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { - // @ts-expect-error FIXME + return this.node(token, { kind: Kind.NAME, value: token.value, }); @@ -201,9 +206,7 @@ export class Parser { * Document : Definition+ */ parseDocument(): DocumentNode { - // @ts-expect-error FIXME: TS Conversion - return this.node(this._lexer.token, { - // @ts-expect-error FIXME: TS Conversion + return this.node(this._lexer.token, { kind: Kind.DOCUMENT, definitions: this.many( TokenKind.SOF, @@ -263,9 +266,7 @@ export class Parser { parseOperationDefinition(): OperationDefinitionNode { const start = this._lexer.token; if (this.peek(TokenKind.BRACE_L)) { - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.OPERATION_DEFINITION, operation: 'query', name: undefined, @@ -279,9 +280,7 @@ export class Parser { if (this.peek(TokenKind.NAME)) { name = this.parseName(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.OPERATION_DEFINITION, operation, name, @@ -323,9 +322,7 @@ export class Parser { * VariableDefinition : Variable : Type DefaultValue? Directives[Const]? */ parseVariableDefinition(): VariableDefinitionNode { - // @ts-expect-error FIXME: TS Conversion - return this.node(this._lexer.token, { - // @ts-expect-error FIXME: TS Conversion + return this.node(this._lexer.token, { kind: Kind.VARIABLE_DEFINITION, variable: this.parseVariable(), type: (this.expectToken(TokenKind.COLON), this.parseTypeReference()), @@ -342,9 +339,7 @@ export class Parser { parseVariable(): VariableNode { const start = this._lexer.token; this.expectToken(TokenKind.DOLLAR); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.VARIABLE, name: this.parseName(), }); @@ -354,9 +349,7 @@ export class Parser { * SelectionSet : { Selection+ } */ parseSelectionSet(): SelectionSetNode { - // @ts-expect-error FIXME: TS Conversion - return this.node(this._lexer.token, { - // @ts-expect-error FIXME: TS Conversion + return this.node(this._lexer.token, { kind: Kind.SELECTION_SET, selections: this.many( TokenKind.BRACE_L, @@ -396,9 +389,7 @@ export class Parser { name = nameOrAlias; } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.FIELD, alias, name, @@ -430,9 +421,7 @@ export class Parser { const name = this.parseName(); this.expectToken(TokenKind.COLON); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.ARGUMENT, name, value: this.parseValueLiteral(isConst), @@ -458,17 +447,13 @@ export class Parser { const hasTypeCondition = this.expectOptionalKeyword('on'); if (!hasTypeCondition && this.peek(TokenKind.NAME)) { - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.FRAGMENT_SPREAD, name: this.parseFragmentName(), directives: this.parseDirectives(false), }); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.INLINE_FRAGMENT, typeCondition: hasTypeCondition ? this.parseNamedType() : undefined, directives: this.parseDirectives(false), @@ -489,9 +474,7 @@ export class Parser { // the grammar of FragmentDefinition: // - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet if (this._options?.allowLegacyFragmentVariables === true) { - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.FRAGMENT_DEFINITION, name: this.parseFragmentName(), variableDefinitions: this.parseVariableDefinitions(), @@ -500,9 +483,7 @@ export class Parser { selectionSet: this.parseSelectionSet(), }); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.FRAGMENT_DEFINITION, name: this.parseFragmentName(), typeCondition: (this.expectKeyword('on'), this.parseNamedType()), @@ -552,17 +533,13 @@ export class Parser { return this.parseObject(isConst); case TokenKind.INT: this._lexer.advance(); - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { - // @ts-expect-error FIXME + return this.node(token, { kind: Kind.INT, value: token.value, }); case TokenKind.FLOAT: this._lexer.advance(); - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { - // @ts-expect-error FIXME + return this.node(token, { kind: Kind.FLOAT, value: token.value, }); @@ -573,18 +550,19 @@ export class Parser { this._lexer.advance(); switch (token.value) { case 'true': - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { kind: Kind.BOOLEAN, value: true }); + return this.node(token, { + kind: Kind.BOOLEAN, + value: true, + }); case 'false': - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { kind: Kind.BOOLEAN, value: false }); + return this.node(token, { + kind: Kind.BOOLEAN, + value: false, + }); case 'null': - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { kind: Kind.NULL }); + return this.node(token, { kind: Kind.NULL }); default: - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { - // @ts-expect-error FIXME + return this.node(token, { kind: Kind.ENUM, value: token.value, }); @@ -615,9 +593,7 @@ export class Parser { parseStringLiteral(): StringValueNode { const token = this._lexer.token; this._lexer.advance(); - // @ts-expect-error FIXME: TS Conversion - return this.node(token, { - // @ts-expect-error FIXME + return this.node(token, { kind: Kind.STRING, value: token.value, block: token.kind === TokenKind.BLOCK_STRING, @@ -633,9 +609,7 @@ export class Parser { parseList(isConst: boolean): ListValueNode; parseList(isConst: boolean): ListValueNode { const item = () => this.parseValueLiteral(isConst); - // @ts-expect-error FIXME: TS Conversion - return this.node(this._lexer.token, { - // @ts-expect-error FIXME: TS Conversion + return this.node(this._lexer.token, { kind: Kind.LIST, values: this.any(TokenKind.BRACKET_L, item, TokenKind.BRACKET_R), }); @@ -650,9 +624,7 @@ export class Parser { parseObject(isConst: boolean): ObjectValueNode; parseObject(isConst: boolean): ObjectValueNode { const item = () => this.parseObjectField(isConst); - // @ts-expect-error FIXME: TS Conversion - return this.node(this._lexer.token, { - // @ts-expect-error FIXME: TS Conversion + return this.node(this._lexer.token, { kind: Kind.OBJECT, fields: this.any(TokenKind.BRACE_L, item, TokenKind.BRACE_R), }); @@ -667,10 +639,7 @@ export class Parser { const start = this._lexer.token; const name = this.parseName(); this.expectToken(TokenKind.COLON); - - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.OBJECT_FIELD, name, value: this.parseValueLiteral(isConst), @@ -704,9 +673,7 @@ export class Parser { parseDirective(isConst: boolean): DirectiveNode { const start = this._lexer.token; this.expectToken(TokenKind.AT); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.DIRECTIVE, name: this.parseName(), arguments: this.parseArguments(isConst), @@ -727,8 +694,7 @@ export class Parser { if (this.expectOptionalToken(TokenKind.BRACKET_L)) { const innerType = this.parseTypeReference(); this.expectToken(TokenKind.BRACKET_R); - type = this.node(start, { - // @ts-expect-error FIXME: TS Conversion + type = this.node(start, { kind: Kind.LIST_TYPE, type: innerType, }); @@ -737,13 +703,12 @@ export class Parser { } if (this.expectOptionalToken(TokenKind.BANG)) { - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.NON_NULL_TYPE, type, }); } + return type; } @@ -751,9 +716,7 @@ export class Parser { * NamedType : Name */ parseNamedType(): NamedTypeNode { - // @ts-expect-error FIXME: TS Conversion - return this.node(this._lexer.token, { - // @ts-expect-error FIXME: TS Conversion + return this.node(this._lexer.token, { kind: Kind.NAMED_TYPE, name: this.parseName(), }); @@ -831,9 +794,7 @@ export class Parser { this.parseOperationTypeDefinition, TokenKind.BRACE_R, ); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.SCHEMA_DEFINITION, description, directives, @@ -849,9 +810,7 @@ export class Parser { const operation = this.parseOperationType(); this.expectToken(TokenKind.COLON); const type = this.parseNamedType(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.OPERATION_TYPE_DEFINITION, operation, type, @@ -867,9 +826,7 @@ export class Parser { this.expectKeyword('scalar'); const name = this.parseName(); const directives = this.parseConstDirectives(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.SCALAR_TYPE_DEFINITION, description, name, @@ -890,9 +847,7 @@ export class Parser { const interfaces = this.parseImplementsInterfaces(); const directives = this.parseConstDirectives(); const fields = this.parseFieldsDefinition(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.OBJECT_TYPE_DEFINITION, description, name, @@ -936,9 +891,7 @@ export class Parser { this.expectToken(TokenKind.COLON); const type = this.parseTypeReference(); const directives = this.parseConstDirectives(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.FIELD_DEFINITION, description, name, @@ -974,9 +927,7 @@ export class Parser { defaultValue = this.parseConstValueLiteral(); } const directives = this.parseConstDirectives(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.INPUT_VALUE_DEFINITION, description, name, @@ -998,9 +949,7 @@ export class Parser { const interfaces = this.parseImplementsInterfaces(); const directives = this.parseConstDirectives(); const fields = this.parseFieldsDefinition(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.INTERFACE_TYPE_DEFINITION, description, name, @@ -1021,9 +970,7 @@ export class Parser { const name = this.parseName(); const directives = this.parseConstDirectives(); const types = this.parseUnionMemberTypes(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.UNION_TYPE_DEFINITION, description, name, @@ -1054,9 +1001,7 @@ export class Parser { const name = this.parseName(); const directives = this.parseConstDirectives(); const values = this.parseEnumValuesDefinition(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.ENUM_TYPE_DEFINITION, description, name, @@ -1086,9 +1031,7 @@ export class Parser { const description = this.parseDescription(); const name = this.parseName(); const directives = this.parseConstDirectives(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.ENUM_VALUE_DEFINITION, description, name, @@ -1107,9 +1050,7 @@ export class Parser { const name = this.parseName(); const directives = this.parseConstDirectives(); const fields = this.parseInputFieldsDefinition(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.INPUT_OBJECT_TYPE_DEFINITION, description, name, @@ -1185,9 +1126,7 @@ export class Parser { if (directives.length === 0 && operationTypes.length === 0) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.SCHEMA_EXTENSION, directives, operationTypes, @@ -1207,9 +1146,7 @@ export class Parser { if (directives.length === 0) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.SCALAR_TYPE_EXTENSION, name, directives, @@ -1237,9 +1174,7 @@ export class Parser { ) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.OBJECT_TYPE_EXTENSION, name, interfaces, @@ -1269,9 +1204,7 @@ export class Parser { ) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.INTERFACE_TYPE_EXTENSION, name, interfaces, @@ -1295,9 +1228,7 @@ export class Parser { if (directives.length === 0 && types.length === 0) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.UNION_TYPE_EXTENSION, name, directives, @@ -1320,9 +1251,7 @@ export class Parser { if (directives.length === 0 && values.length === 0) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.ENUM_TYPE_EXTENSION, name, directives, @@ -1345,9 +1274,7 @@ export class Parser { if (directives.length === 0 && fields.length === 0) { throw this.unexpected(); } - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.INPUT_OBJECT_TYPE_EXTENSION, name, directives, @@ -1369,9 +1296,7 @@ export class Parser { const repeatable = this.expectOptionalKeyword('repeatable'); this.expectKeyword('on'); const locations = this.parseDirectiveLocations(); - // @ts-expect-error FIXME: TS Conversion - return this.node(start, { - // @ts-expect-error FIXME: TS Conversion + return this.node(start, { kind: Kind.DIRECTIVE_DEFINITION, description, name, @@ -1420,7 +1345,7 @@ export class Parser { parseDirectiveLocation(): NameNode { const start = this._lexer.token; const name = this.parseName(); - if (DirectiveLocation[name.value] !== undefined) { + if (Object.prototype.hasOwnProperty.call(DirectiveLocation, name.value)) { return name; } throw this.unexpected(start); diff --git a/src/language/printLocation.ts b/src/language/printLocation.ts index 4e1c926447..97a34f14b4 100644 --- a/src/language/printLocation.ts +++ b/src/language/printLocation.ts @@ -47,11 +47,10 @@ export function printSourceLocation( locationStr + printPrefixedLines([ [`${lineNum} |`, subLines[0]], - // @ts-expect-error FIXME: TS Conversion - ...subLines.slice(1, subLineIndex + 1).map((subLine) => ['|', subLine]), - // @ts-expect-error FIXME: TS Conversion + ...subLines + .slice(1, subLineIndex + 1) + .map((subLine) => ['|', subLine] as const), ['|', '^'.padStart(subLineColumnNum)], - // @ts-expect-error FIXME: TS Conversion ['|', subLines[subLineIndex + 1]], ]) ); @@ -69,7 +68,9 @@ export function printSourceLocation( ); } -function printPrefixedLines(lines: ReadonlyArray<[string, string]>): string { +function printPrefixedLines( + lines: ReadonlyArray, +): string { const existingLines = lines.filter(([_, line]) => line !== undefined); const padLen = Math.max(...existingLines.map(([prefix]) => prefix.length)); diff --git a/src/language/printer.ts b/src/language/printer.ts index eae84d7fe3..0d907fca39 100644 --- a/src/language/printer.ts +++ b/src/language/printer.ts @@ -1,7 +1,7 @@ import type { Maybe } from '../jsutils/Maybe'; import type { ASTNode } from './ast'; - +import type { ASTReducer } from './visitor'; import { visit } from './visitor'; import { printBlockString } from './blockString'; @@ -15,8 +15,7 @@ export function print(ast: ASTNode): string { const MAX_LINE_LENGTH = 80; -// TODO: provide better type coverage in future -const printDocASTReducer: any = { +const printDocASTReducer: ASTReducer = { Name: { leave: (node) => node.value }, Variable: { leave: (node) => '$' + node.name }, @@ -309,7 +308,10 @@ const printDocASTReducer: any = { * Given maybeArray, print an empty string if it is null or empty, otherwise * print all items together separated by separator if provided */ -function join(maybeArray: Maybe>, separator = ''): string { +function join( + maybeArray: Maybe>, + separator = '', +): string { return maybeArray?.filter((x) => x).join(separator) ?? ''; } @@ -317,7 +319,7 @@ function join(maybeArray: Maybe>, separator = ''): string { * Given array, print each item on its own line, wrapped in an * indented "{ }" block. */ -function block(array: Maybe>): string { +function block(array: Maybe>): string { return wrap('{\n', indent(join(array, '\n')), '\n}'); } @@ -338,7 +340,7 @@ function indent(str: string): string { return wrap(' ', str.replace(/\n/g, '\n ')); } -function hasMultilineItems(maybeArray: Maybe>): boolean { +function hasMultilineItems(maybeArray: Maybe>): boolean { // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') return maybeArray?.some((str) => str.includes('\n')) ?? false; } diff --git a/src/language/visitor.ts b/src/language/visitor.ts index c4cd8d422f..c6ffa4c70b 100644 --- a/src/language/visitor.ts +++ b/src/language/visitor.ts @@ -8,7 +8,7 @@ import { isNode } from './ast'; * A visitor is provided to visit, it contains the collection of * relevant functions to be called during the visitor's traversal. */ -export type ASTVisitor = EnterLeaveVisitor & KindVisitor; +export type ASTVisitor = EnterLeaveVisitor | KindVisitor; type KindVisitor = { readonly [K in keyof ASTKindToNode]?: @@ -42,6 +42,40 @@ export type ASTVisitFn = ( ancestors: ReadonlyArray>, ) => any; +/** + * A reducer is comprised of reducer functions which convert AST nodes into + * another form. + */ +export type ASTReducer = { + readonly [K in keyof ASTKindToNode]?: { + readonly enter?: ASTVisitFn; + readonly leave: ASTReducerFn; + }; +}; + +type ASTReducerFn = ( + /** The current node being visiting. */ + node: { [K in keyof TReducedNode]: ReducedField }, + /** The index or key to this node from the parent node or Array. */ + key: string | number | undefined, + /** The parent immediately above this node, which may be an Array. */ + parent: ASTNode | ReadonlyArray | undefined, + /** The key path to get to this node from the root node. */ + path: ReadonlyArray, + /** + * All nodes and Arrays visited before reaching parent of this node. + * These correspond to array indices in `path`. + * Note: ancestors includes arrays which contain the parent of visited node. + */ + ancestors: ReadonlyArray>, +) => R; + +type ReducedField = T extends null | undefined + ? T + : T extends ReadonlyArray + ? ReadonlyArray + : R; + const QueryDocumentKeys = { Name: [], @@ -201,7 +235,12 @@ export const BREAK: unknown = Object.freeze({}); * } * }) */ -export function visit(root: ASTNode, visitor: ASTVisitor): any { +export function visit(root: N, visitor: ASTVisitor): N; +export function visit(root: ASTNode, visitor: ASTReducer): R; +export function visit( + root: ASTNode, + visitor: ASTVisitor | ASTReducer, +): any { /* eslint-disable no-undef-init */ let stack: any = undefined; let inArray = Array.isArray(root); @@ -300,7 +339,7 @@ export function visit(root: ASTNode, visitor: ASTVisitor): any { } else { stack = { inArray, index, keys, edits, prev: stack }; inArray = Array.isArray(node); - keys = inArray ? node : QueryDocumentKeys[node.kind] ?? []; + keys = inArray ? node : (QueryDocumentKeys as any)[node.kind] ?? []; index = -1; edits = []; if (parent) { @@ -329,12 +368,13 @@ export function visitInParallel( const skipping = new Array(visitors.length); return { - enter(node) { + enter(...args) { + const node = args[0]; for (let i = 0; i < visitors.length; i++) { if (skipping[i] == null) { const fn = getVisitFn(visitors[i], node.kind, /* isLeaving */ false); if (fn) { - const result = fn.apply(visitors[i], arguments); + const result = fn.apply(visitors[i], args); if (result === false) { skipping[i] = node; } else if (result === BREAK) { @@ -346,12 +386,13 @@ export function visitInParallel( } } }, - leave(node) { + leave(...args) { + const node = args[0]; for (let i = 0; i < visitors.length; i++) { if (skipping[i] == null) { const fn = getVisitFn(visitors[i], node.kind, /* isLeaving */ true); if (fn) { - const result = fn.apply(visitors[i], arguments); + const result = fn.apply(visitors[i], args); if (result === BREAK) { skipping[i] = BREAK; } else if (result !== undefined && result !== false) { @@ -372,27 +413,21 @@ export function visitInParallel( */ export function getVisitFn( visitor: ASTVisitor, - kind: string, + kind: keyof ASTKindToNode, isLeaving: boolean, ): Maybe> { - const kindVisitor = visitor[kind]; + const kindVisitor: + | ASTVisitFn + | EnterLeaveVisitor + | undefined = (visitor as any)[kind]; if (kindVisitor) { - if (!isLeaving && typeof kindVisitor === 'function') { + if (typeof kindVisitor === 'function') { // { Kind() {} } - return kindVisitor; - } - const kindSpecificVisitor = isLeaving - ? kindVisitor.leave - : kindVisitor.enter; - if (typeof kindSpecificVisitor === 'function') { - // { Kind: { enter() {}, leave() {} } } - return kindSpecificVisitor; - } - } else { - const specificVisitor = isLeaving ? visitor.leave : visitor.enter; - if (specificVisitor) { - // { enter() {}, leave() {} } - return specificVisitor; + return isLeaving ? undefined : kindVisitor; } + // { Kind: { enter() {}, leave() {} } } + return isLeaving ? kindVisitor.leave : kindVisitor.enter; } + // { enter() {}, leave() {} } + return isLeaving ? (visitor as any).leave : (visitor as any).enter; } From 946d06a53b8e4f07e3ab92c4ad6b0daf82172813 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 22:10:28 -0700 Subject: [PATCH 6/9] TS: Fix strict issues in src/jsutils --- src/jsutils/__tests__/identityFunc-test.ts | 2 +- src/jsutils/__tests__/inspect-test.ts | 21 ++++++++++++++------- src/jsutils/didYouMean.ts | 7 +++---- src/jsutils/inspect.ts | 16 +++++++++------- src/jsutils/isAsyncIterable.ts | 2 +- src/jsutils/isIterableObject.ts | 2 +- src/jsutils/isPromise.ts | 5 ++--- src/jsutils/memoize3.ts | 2 +- 8 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/jsutils/__tests__/identityFunc-test.ts b/src/jsutils/__tests__/identityFunc-test.ts index 625e20c457..97cc25eb2f 100644 --- a/src/jsutils/__tests__/identityFunc-test.ts +++ b/src/jsutils/__tests__/identityFunc-test.ts @@ -5,7 +5,7 @@ import { identityFunc } from '../identityFunc'; describe('identityFunc', () => { it('returns the first argument it receives', () => { - // @ts-expect-error FIXME: TS Conversion + // @ts-expect-error (Expects an argument) expect(identityFunc()).to.equal(undefined); expect(identityFunc(undefined)).to.equal(undefined); expect(identityFunc(null)).to.equal(null); diff --git a/src/jsutils/__tests__/inspect-test.ts b/src/jsutils/__tests__/inspect-test.ts index a4f17177ac..e174df613a 100644 --- a/src/jsutils/__tests__/inspect-test.ts +++ b/src/jsutils/__tests__/inspect-test.ts @@ -132,13 +132,13 @@ describe('inspect', () => { '{ self: [Circular], deepSelf: { self: [Circular] } }', ); - const array = []; + const array: any = []; array[0] = array; array[1] = [array]; expect(inspect(array)).to.equal('[[Circular], [[Circular]]]'); - const mixed = { array: [] }; + const mixed: any = { array: [] }; mixed.array[0] = mixed; expect(inspect(mixed)).to.equal('{ array: [[Circular]] }'); @@ -165,14 +165,21 @@ describe('inspect', () => { expect(inspect([[new Foo()]])).to.equal('[[[Foo]]]'); - Foo.prototype[Symbol.toStringTag] = 'Bar'; - expect(inspect([[new Foo()]])).to.equal('[[[Bar]]]'); + class Foo2 { + foo: string; + + [Symbol.toStringTag] = 'Bar'; + + constructor() { + this.foo = 'bar'; + } + } + expect(inspect([[new Foo2()]])).to.equal('[[[Bar]]]'); // eslint-disable-next-line func-names - const objectWithoutClassName = new (function () { - // eslint-disable-next-line @typescript-eslint/no-invalid-this + const objectWithoutClassName = new (function (this: any) { this.foo = 1; - })(); + } as any)(); expect(inspect([[objectWithoutClassName]])).to.equal('[[[Object]]]'); }); }); diff --git a/src/jsutils/didYouMean.ts b/src/jsutils/didYouMean.ts index 4f15678975..33e10a42c1 100644 --- a/src/jsutils/didYouMean.ts +++ b/src/jsutils/didYouMean.ts @@ -12,10 +12,9 @@ export function didYouMean( firstArg: string | ReadonlyArray, secondArg?: ReadonlyArray, ) { - const [subMessage, suggestionsArg] = - typeof firstArg === 'string' - ? [firstArg, secondArg] - : [undefined, firstArg]; + const [subMessage, suggestionsArg] = secondArg + ? [firstArg as string, secondArg] + : [undefined, firstArg as ReadonlyArray]; let message = ' Did you mean '; if (subMessage) { diff --git a/src/jsutils/inspect.ts b/src/jsutils/inspect.ts index 4616edc316..9076490de0 100644 --- a/src/jsutils/inspect.ts +++ b/src/jsutils/inspect.ts @@ -22,7 +22,7 @@ function formatValue(value: unknown, seenValues: Array): string { } function formatObjectValue( - value: Object, + value: object | null, previouslySeenValues: Array, ): string { if (value === null) { @@ -35,10 +35,8 @@ function formatObjectValue( const seenValues = [...previouslySeenValues, value]; - // @ts-expect-error FIXME: TS Conversion - if (typeof value.toJSON === 'function') { - // @ts-expect-error FIXME: TS Conversion - const jsonValue = (value.toJSON as () => unknown)(); + if (isJSONable(value)) { + const jsonValue = value.toJSON(); // check for infinite recursion if (jsonValue !== value) { @@ -53,7 +51,11 @@ function formatObjectValue( return formatObject(value, seenValues); } -function formatObject(object: Object, seenValues: Array): string { +function isJSONable(value: any): value is { toJSON: () => unknown } { + return typeof value.toJSON === 'function'; +} + +function formatObject(object: object, seenValues: Array): string { const entries = Object.entries(object); if (entries.length === 0) { return '{}'; @@ -98,7 +100,7 @@ function formatArray( return '[' + items.join(', ') + ']'; } -function getObjectTag(object: Object): string { +function getObjectTag(object: object): string { const tag = Object.prototype.toString .call(object) .replace(/^\[object /, '') diff --git a/src/jsutils/isAsyncIterable.ts b/src/jsutils/isAsyncIterable.ts index 8f26130003..0eb4ab1d6e 100644 --- a/src/jsutils/isAsyncIterable.ts +++ b/src/jsutils/isAsyncIterable.ts @@ -3,7 +3,7 @@ * implementing a `Symbol.asyncIterator` method. */ export function isAsyncIterable( - maybeAsyncIterable: unknown, + maybeAsyncIterable: any, ): maybeAsyncIterable is AsyncIterable { return typeof maybeAsyncIterable?.[Symbol.asyncIterator] === 'function'; } diff --git a/src/jsutils/isIterableObject.ts b/src/jsutils/isIterableObject.ts index 032b28ae51..ac0cd2576c 100644 --- a/src/jsutils/isIterableObject.ts +++ b/src/jsutils/isIterableObject.ts @@ -15,7 +15,7 @@ * isIterableObject({ length: 1, 0: 'Alpha' }) // false */ export function isIterableObject( - maybeIterable: unknown, + maybeIterable: any, ): maybeIterable is Iterable { return ( typeof maybeIterable === 'object' && diff --git a/src/jsutils/isPromise.ts b/src/jsutils/isPromise.ts index 69b0a9910e..5fc3c10458 100644 --- a/src/jsutils/isPromise.ts +++ b/src/jsutils/isPromise.ts @@ -2,7 +2,6 @@ * Returns true if the value acts like a Promise, i.e. has a "then" function, * otherwise returns false. */ -export function isPromise(value: unknown): value is Promise { - // eslint-disable-next-line @typescript-eslint/dot-notation - return typeof value?.['then'] === 'function'; +export function isPromise(value: any): value is Promise { + return typeof value?.then === 'function'; } diff --git a/src/jsutils/memoize3.ts b/src/jsutils/memoize3.ts index e6ac2faf1d..213cb95d10 100644 --- a/src/jsutils/memoize3.ts +++ b/src/jsutils/memoize3.ts @@ -7,7 +7,7 @@ export function memoize3< A3 extends object, R, >(fn: (a1: A1, a2: A2, a3: A3) => R): (a1: A1, a2: A2, a3: A3) => R { - let cache0; + let cache0: WeakMap>>; return function memoized(a1, a2, a3) { if (cache0 === undefined) { From 111a4819f4be9b1eabda3f465f8188624eda84c7 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 22:18:18 -0700 Subject: [PATCH 7/9] TS: Fix strict issues in src/execution --- src/execution/__tests__/executor-test.ts | 6 +++--- src/execution/__tests__/schema-test.ts | 2 +- src/execution/__tests__/union-interface-test.ts | 14 +++++++------- src/execution/__tests__/variables-test.ts | 9 +++++++-- src/execution/execute.ts | 4 +--- src/execution/values.ts | 4 ++-- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/execution/__tests__/executor-test.ts b/src/execution/__tests__/executor-test.ts index b886270978..5283aa4de5 100644 --- a/src/execution/__tests__/executor-test.ts +++ b/src/execution/__tests__/executor-test.ts @@ -94,7 +94,7 @@ describe('Execute: Handles basic execution tasks', () => { return Promise.resolve(data); } - const DataType = new GraphQLObjectType({ + const DataType: GraphQLObjectType = new GraphQLObjectType({ name: 'DataType', fields: () => ({ a: { type: GraphQLString }, @@ -185,7 +185,7 @@ describe('Execute: Handles basic execution tasks', () => { }); it('merges parallel fragments', () => { - const Type = new GraphQLObjectType({ + const Type: GraphQLObjectType = new GraphQLObjectType({ name: 'Type', fields: () => ({ a: { type: GraphQLString, resolve: () => 'Apple' }, @@ -624,7 +624,7 @@ describe('Execute: Handles basic execution tasks', () => { }); it('Full response path is included for non-nullable fields', () => { - const A = new GraphQLObjectType({ + const A: GraphQLObjectType = new GraphQLObjectType({ name: 'A', fields: () => ({ nullableA: { diff --git a/src/execution/__tests__/schema-test.ts b/src/execution/__tests__/schema-test.ts index 7da7849c5a..b954fd98db 100644 --- a/src/execution/__tests__/schema-test.ts +++ b/src/execution/__tests__/schema-test.ts @@ -29,7 +29,7 @@ describe('Execute: Handles execution with a complex schema', () => { }, }); - const BlogAuthor = new GraphQLObjectType({ + const BlogAuthor: GraphQLObjectType = new GraphQLObjectType({ name: 'Author', fields: () => ({ id: { type: GraphQLString }, diff --git a/src/execution/__tests__/union-interface-test.ts b/src/execution/__tests__/union-interface-test.ts index d65ada60c5..7ff9bd72bb 100644 --- a/src/execution/__tests__/union-interface-test.ts +++ b/src/execution/__tests__/union-interface-test.ts @@ -65,14 +65,14 @@ const NamedType = new GraphQLInterfaceType({ }, }); -const LifeType = new GraphQLInterfaceType({ +const LifeType: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Life', fields: () => ({ progeny: { type: new GraphQLList(LifeType) }, }), }); -const MammalType = new GraphQLInterfaceType({ +const MammalType: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Mammal', interfaces: [LifeType], fields: () => ({ @@ -82,7 +82,7 @@ const MammalType = new GraphQLInterfaceType({ }), }); -const DogType = new GraphQLObjectType({ +const DogType: GraphQLObjectType = new GraphQLObjectType({ name: 'Dog', interfaces: [MammalType, LifeType, NamedType], fields: () => ({ @@ -95,7 +95,7 @@ const DogType = new GraphQLObjectType({ isTypeOf: (value) => value instanceof Dog, }); -const CatType = new GraphQLObjectType({ +const CatType: GraphQLObjectType = new GraphQLObjectType({ name: 'Cat', interfaces: [MammalType, LifeType, NamedType], fields: () => ({ @@ -125,7 +125,7 @@ const PetType = new GraphQLUnionType({ }, }); -const PersonType = new GraphQLObjectType({ +const PersonType: GraphQLObjectType = new GraphQLObjectType({ name: 'Person', interfaces: [NamedType, MammalType, LifeType], fields: () => ({ @@ -503,7 +503,7 @@ describe('Execute: Union and intersection types', () => { let encounteredSchema; let encounteredRootValue; - const NamedType2 = new GraphQLInterfaceType({ + const NamedType2: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Named', fields: { name: { type: GraphQLString }, @@ -516,7 +516,7 @@ describe('Execute: Union and intersection types', () => { }, }); - const PersonType2 = new GraphQLObjectType({ + const PersonType2: GraphQLObjectType = new GraphQLObjectType({ name: 'Person', interfaces: [NamedType2], fields: { diff --git a/src/execution/__tests__/variables-test.ts b/src/execution/__tests__/variables-test.ts index ad71465568..ebf7d0b169 100644 --- a/src/execution/__tests__/variables-test.ts +++ b/src/execution/__tests__/variables-test.ts @@ -7,7 +7,10 @@ import { invariant } from '../../jsutils/invariant'; import { Kind } from '../../language/kinds'; import { parse } from '../../language/parser'; -import type { GraphQLArgumentConfig } from '../../type/definition'; +import type { + GraphQLFieldConfig, + GraphQLArgumentConfig, +} from '../../type/definition'; import { GraphQLSchema } from '../../type/schema'; import { GraphQLString } from '../../type/scalars'; import { @@ -64,7 +67,9 @@ const TestEnum = new GraphQLEnumType({ }, }); -function fieldWithInputArg(inputArg: GraphQLArgumentConfig) { +function fieldWithInputArg( + inputArg: GraphQLArgumentConfig, +): GraphQLFieldConfig { return { type: GraphQLString, args: { input: inputArg }, diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 923d018d1a..5d7f25e722 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -180,7 +180,7 @@ export function execute(args: ExecutionArgs): PromiseOrValue { ); // Return early errors if execution context failed. - if (Array.isArray(exeContext)) { + if (!('schema' in exeContext)) { return { errors: exeContext }; } @@ -191,9 +191,7 @@ export function execute(args: ExecutionArgs): PromiseOrValue { // field and its descendants will be omitted, and sibling fields will still // be executed. An execution which encounters errors will still result in a // resolved Promise. - // @ts-expect-error FIXME: TS Conversion const data = executeOperation(exeContext, exeContext.operation, rootValue); - // @ts-expect-error FIXME: TS Conversion return buildResponse(exeContext, data); } diff --git a/src/execution/values.ts b/src/execution/values.ts index 05277f3827..6d4c95b85a 100644 --- a/src/execution/values.ts +++ b/src/execution/values.ts @@ -77,7 +77,7 @@ function coerceVariableValues( inputs: { readonly [variable: string]: unknown }, onError: (error: GraphQLError) => void, ): { [variable: string]: unknown } { - const coercedValues = {}; + const coercedValues: { [variable: string]: unknown } = {}; for (const varDefNode of varDefNodes) { const varName = varDefNode.variable.name.value; const varType = typeFromAST(schema, varDefNode.type); @@ -162,7 +162,7 @@ export function getArgumentValues( node: FieldNode | DirectiveNode, variableValues?: Maybe>, ): { [argument: string]: unknown } { - const coercedValues = {}; + const coercedValues: { [argument: string]: unknown } = {}; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203') const argumentNodes = node.arguments ?? []; From 25bd31c97ad87575e9e3e3e4a6c131c762ccfc15 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 22:23:48 -0700 Subject: [PATCH 8/9] TS: Fix strict issues in src/error --- src/__tests__/starWarsSchema.ts | 2 +- src/error/GraphQLError.ts | 9 --------- src/error/__tests__/formatError-test.ts | 2 ++ src/error/locatedError.ts | 17 ++++++++--------- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/__tests__/starWarsSchema.ts b/src/__tests__/starWarsSchema.ts index 16a80fcee7..e915c98f39 100644 --- a/src/__tests__/starWarsSchema.ts +++ b/src/__tests__/starWarsSchema.ts @@ -98,7 +98,7 @@ const episodeEnum = new GraphQLEnumType({ * secretBackstory: String * } */ -const characterInterface = new GraphQLInterfaceType({ +const characterInterface: GraphQLInterfaceType = new GraphQLInterfaceType({ name: 'Character', description: 'A character in the Star Wars Trilogy', fields: () => ({ diff --git a/src/error/GraphQLError.ts b/src/error/GraphQLError.ts index 68ddeda702..89a82eef29 100644 --- a/src/error/GraphQLError.ts +++ b/src/error/GraphQLError.ts @@ -14,15 +14,6 @@ import { printLocation, printSourceLocation } from '../language/printLocation'; * GraphQL document and/or execution result that correspond to the Error. */ export class GraphQLError extends Error { - /** - * A message describing the Error for debugging purposes. - * - * Enumerable, and appears in the result of JSON.stringify(). - * - * Note: should be treated as readonly, despite invariant usage. - */ - message: string; - /** * An array of { line, column } locations within the source GraphQL document * which correspond to this error. diff --git a/src/error/__tests__/formatError-test.ts b/src/error/__tests__/formatError-test.ts index d618f8d2d0..9ea5e946ef 100644 --- a/src/error/__tests__/formatError-test.ts +++ b/src/error/__tests__/formatError-test.ts @@ -45,10 +45,12 @@ describe('formatError: default error formatter', () => { }); it('rejects null and undefined errors', () => { + // TODO ts-expect-error (formatError expects a value) expect(() => formatError(undefined)).to.throw( 'Received null or undefined error.', ); + // TODO ts-expect-error (formatError expects a value) expect(() => formatError(null)).to.throw( 'Received null or undefined error.', ); diff --git a/src/error/locatedError.ts b/src/error/locatedError.ts index 286408f792..fb5df58ec7 100644 --- a/src/error/locatedError.ts +++ b/src/error/locatedError.ts @@ -22,21 +22,20 @@ export function locatedError( : new Error('Unexpected error value: ' + inspect(rawOriginalError)); // Note: this uses a brand-check to support GraphQL errors originating from other contexts. - // @ts-expect-error FIXME: TS Conversion - if (Array.isArray(originalError.path)) { - // @ts-expect-error + if (isLocatedGraphQLError(originalError)) { return originalError; } return new GraphQLError( originalError.message, - // @ts-expect-error FIXME - originalError.nodes ?? nodes, - // @ts-expect-error FIXME - originalError.source, - // @ts-expect-error FIXME - originalError.positions, + (originalError as GraphQLError).nodes ?? nodes, + (originalError as GraphQLError).source, + (originalError as GraphQLError).positions, path, originalError, ); } + +function isLocatedGraphQLError(error: any): error is GraphQLError { + return Array.isArray(error.path); +} From 10bbbbd82735a8a1838d7e65b7875680d0b2319f Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Wed, 26 May 2021 22:27:24 -0700 Subject: [PATCH 9/9] TS: Enable strict mode --- src/error/__tests__/formatError-test.ts | 4 ++-- src/execution/__tests__/abstract-test.ts | 6 +++--- src/language/ast.ts | 2 +- src/subscription/__tests__/subscribe-test.ts | 4 ++-- src/subscription/mapAsyncIterator.ts | 1 - src/type/__tests__/definition-test.ts | 12 ++++++------ src/type/__tests__/validation-test.ts | 10 +++++----- src/utilities/__tests__/buildASTSchema-test.ts | 2 +- src/utilities/__tests__/buildClientSchema-test.ts | 2 +- src/utilities/__tests__/extendSchema-test.ts | 2 +- src/utilities/extendSchema.ts | 2 +- src/validation/__tests__/validation-test.ts | 2 +- tsconfig.json | 5 +---- 13 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/error/__tests__/formatError-test.ts b/src/error/__tests__/formatError-test.ts index 9ea5e946ef..b0798c958e 100644 --- a/src/error/__tests__/formatError-test.ts +++ b/src/error/__tests__/formatError-test.ts @@ -45,12 +45,12 @@ describe('formatError: default error formatter', () => { }); it('rejects null and undefined errors', () => { - // TODO ts-expect-error (formatError expects a value) + // @ts-expect-error (formatError expects a value) expect(() => formatError(undefined)).to.throw( 'Received null or undefined error.', ); - // TODO ts-expect-error (formatError expects a value) + // @ts-expect-error (formatError expects a value) expect(() => formatError(null)).to.throw( 'Received null or undefined error.', ); diff --git a/src/execution/__tests__/abstract-test.ts b/src/execution/__tests__/abstract-test.ts index bfbd666b08..0dd097f527 100644 --- a/src/execution/__tests__/abstract-test.ts +++ b/src/execution/__tests__/abstract-test.ts @@ -577,9 +577,9 @@ describe('Execute: Handles execution of abstract types', () => { ); // FIXME: workaround since we can't inject resolveType into SDL - assertInterfaceType(schema.getType('Pet')).resolveType = - // @ts-expect-error - () => schema.getType('Cat'); + // @ts-expect-error + assertInterfaceType(schema.getType('Pet')).resolveType = () => + schema.getType('Cat'); expectError({ forTypeName: undefined }).toEqual( 'Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.', ); diff --git a/src/language/ast.ts b/src/language/ast.ts index 5e72533c28..77cdf06de5 100644 --- a/src/language/ast.ts +++ b/src/language/ast.ts @@ -104,7 +104,7 @@ export class Token { this.end = end; this.line = line; this.column = column; - this.value = value; + this.value = value as string; this.prev = prev; this.next = null; } diff --git a/src/subscription/__tests__/subscribe-test.ts b/src/subscription/__tests__/subscribe-test.ts index dcb02e042c..85d6400a3d 100644 --- a/src/subscription/__tests__/subscribe-test.ts +++ b/src/subscription/__tests__/subscribe-test.ts @@ -313,7 +313,7 @@ describe('Subscription Initialization Phase', () => { }), }); - // TODO ts-expect-error (schema must not be null) + // @ts-expect-error (schema must not be null) (await expectPromise(subscribe({ schema: null, document }))).toRejectWith( 'Expected null to be a GraphQL schema.', ); @@ -323,7 +323,7 @@ describe('Subscription Initialization Phase', () => { 'Expected undefined to be a GraphQL schema.', ); - // TODO ts-expect-error (document must not be null) + // @ts-expect-error (document must not be null) (await expectPromise(subscribe({ schema, document: null }))).toRejectWith( 'Must provide document.', ); diff --git a/src/subscription/mapAsyncIterator.ts b/src/subscription/mapAsyncIterator.ts index 55fc3b9499..3ea9ea8745 100644 --- a/src/subscription/mapAsyncIterator.ts +++ b/src/subscription/mapAsyncIterator.ts @@ -18,7 +18,6 @@ export function mapAsyncIterator( } try { - // @ts-expect-error FIXME: TS Conversion return { value: await callback(result.value), done: false }; } catch (error) { // istanbul ignore else (FIXME: add test case) diff --git a/src/type/__tests__/definition-test.ts b/src/type/__tests__/definition-test.ts index 6011daaef9..1cf4e4f397 100644 --- a/src/type/__tests__/definition-test.ts +++ b/src/type/__tests__/definition-test.ts @@ -333,7 +333,7 @@ describe('Type System: Objects', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', fields: { - // TODO ts-expect-error (must not be undefined) + // @ts-expect-error (must not be undefined) f: undefined, }, }); @@ -697,7 +697,7 @@ describe('Type System: Enums', () => { () => new GraphQLEnumType({ name: 'SomeEnum', - // TODO ts-expect-error (must not be null) + // @ts-expect-error (must not be null) values: { FOO: null }, }), ).to.throw( @@ -843,9 +843,9 @@ describe('Type System: List', () => { expectList(String).to.throw( 'Expected [function String] to be a GraphQL type.', ); - // TODO ts-expect-error (must provide type) + // @ts-expect-error (must provide type) expectList(null).to.throw('Expected null to be a GraphQL type.'); - // TODO ts-expect-error (must provide type) + // @ts-expect-error (must provide type) expectList(undefined).to.throw('Expected undefined to be a GraphQL type.'); }); }); @@ -876,11 +876,11 @@ describe('Type System: Non-Null', () => { expectNonNull(String).to.throw( 'Expected [function String] to be a GraphQL nullable type.', ); - // TODO ts-expect-error (must provide type) + // @ts-expect-error (must provide type) expectNonNull(null).to.throw( 'Expected null to be a GraphQL nullable type.', ); - // TODO ts-expect-error (must provide type) + // @ts-expect-error (must provide type) expectNonNull(undefined).to.throw( 'Expected undefined to be a GraphQL nullable type.', ); diff --git a/src/type/__tests__/validation-test.ts b/src/type/__tests__/validation-test.ts index 61d43a6030..6d8ef8f789 100644 --- a/src/type/__tests__/validation-test.ts +++ b/src/type/__tests__/validation-test.ts @@ -1036,7 +1036,7 @@ describe('Type System: Object fields must have output types', () => { } it('rejects an empty Object field type', () => { - // TODO ts-expect-error (type field must not be undefined) + // @ts-expect-error (type field must not be undefined) const schema = schemaWithObjectField({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { @@ -1098,7 +1098,7 @@ describe('Type System: Objects can only implement unique interfaces', () => { const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'BadObject', - // TODO ts-expect-error (interfaces must not contain undefined) + // @ts-expect-error (interfaces must not contain undefined) interfaces: [undefined], fields: { f: { type: GraphQLString } }, }), @@ -1357,7 +1357,7 @@ describe('Type System: Interface fields must have output types', () => { } it('rejects an empty Interface field type', () => { - // TODO ts-expect-error (type field must not be undefined) + // @ts-expect-error (type field must not be undefined) const schema = schemaWithInterfaceField({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { @@ -1493,7 +1493,7 @@ describe('Type System: Arguments must have input types', () => { } it('rejects an empty field arg type', () => { - // TODO ts-expect-error (type field must not be undefined) + // @ts-expect-error (type field must not be undefined) const schema = schemaWithArg({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { @@ -1631,7 +1631,7 @@ describe('Type System: Input Object fields must have input types', () => { } it('rejects an empty input field type', () => { - // TODO ts-expect-error (type field must not be undefined) + // @ts-expect-error (type field must not be undefined) const schema = schemaWithInputField({ type: undefined }); expect(validateSchema(schema)).to.deep.equal([ { diff --git a/src/utilities/__tests__/buildASTSchema-test.ts b/src/utilities/__tests__/buildASTSchema-test.ts index 69d09c6705..5cc4121def 100644 --- a/src/utilities/__tests__/buildASTSchema-test.ts +++ b/src/utilities/__tests__/buildASTSchema-test.ts @@ -1095,7 +1095,7 @@ describe('Schema Builder', () => { }); it('Rejects invalid AST', () => { - // TODO ts-expect-error (First parameter expected to be DocumentNode) + // @ts-expect-error (First parameter expected to be DocumentNode) expect(() => buildASTSchema(null)).to.throw( 'Must provide valid Document AST', ); diff --git a/src/utilities/__tests__/buildClientSchema-test.ts b/src/utilities/__tests__/buildClientSchema-test.ts index aa516bf7a1..d65f69b54a 100644 --- a/src/utilities/__tests__/buildClientSchema-test.ts +++ b/src/utilities/__tests__/buildClientSchema-test.ts @@ -629,7 +629,7 @@ describe('Type System: build schema from introspection', () => { `); it('throws when introspection is missing __schema property', () => { - // TODO ts-expect-error (First parameter expected to be introspection results) + // @ts-expect-error (First parameter expected to be introspection results) expect(() => buildClientSchema(null)).to.throw( 'Invalid or incomplete introspection result. Ensure that you are passing "data" property of introspection response and no "errors" was returned alongside: null.', ); diff --git a/src/utilities/__tests__/extendSchema-test.ts b/src/utilities/__tests__/extendSchema-test.ts index d0d51ed505..022d6a5813 100644 --- a/src/utilities/__tests__/extendSchema-test.ts +++ b/src/utilities/__tests__/extendSchema-test.ts @@ -1143,7 +1143,7 @@ describe('extendSchema', () => { it('Rejects invalid AST', () => { const schema = new GraphQLSchema({}); - // TODO ts-expect-error (Second argument expects DocumentNode) + // @ts-expect-error (Second argument expects DocumentNode) expect(() => extendSchema(schema, null)).to.throw( 'Must provide valid Document AST', ); diff --git a/src/utilities/extendSchema.ts b/src/utilities/extendSchema.ts index 5ad1583597..7b82e2ce39 100644 --- a/src/utilities/extendSchema.ts +++ b/src/utilities/extendSchema.ts @@ -406,7 +406,7 @@ export function extendSchemaImpl( // Note: While this could make early assertions to get the correctly // typed values below, that would throw immediately while type system // validation with validateSchema() will produce more actionable results. - // TODO ts-expect-error + // @ts-expect-error opTypes[operationType.operation] = getNamedType(operationType.type); } } diff --git a/src/validation/__tests__/validation-test.ts b/src/validation/__tests__/validation-test.ts index 42860051fc..ed68d68788 100644 --- a/src/validation/__tests__/validation-test.ts +++ b/src/validation/__tests__/validation-test.ts @@ -16,7 +16,7 @@ import { testSchema } from './harness'; describe('Validate: Supports full validation', () => { it('rejects invalid documents', () => { - // TODO ts-expect-error (expects a DocumentNode as a second parameter) + // @ts-expect-error (expects a DocumentNode as a second parameter) expect(() => validate(testSchema, null)).to.throw('Must provide document.'); }); diff --git a/tsconfig.json b/tsconfig.json index d160d317ef..c76ea4574f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,10 +4,7 @@ "module": "commonjs", "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], "target": "es2019", - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": false, - "strictFunctionTypes": true, + "strict": true, "noEmit": true, "forceConsistentCasingInFileNames": true }