Skip to content

Commit

Permalink
fix: Remove redundant constraints on IntrospectionQuery causing TS pe…
Browse files Browse the repository at this point in the history
…rf bottlenecks (#26)
  • Loading branch information
kitten authored Jan 18, 2024
1 parent 3a06a4c commit d383684
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 57 deletions.
5 changes: 5 additions & 0 deletions .changeset/tiny-cameras-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'gql.tada': patch
---

Remove redundant constraint on `IntrospectionQuery` data. When the full type is used as an `extends`, the input type (which can be a huge schema), is checked against this type, which forces a full evaluation. This means that TypeScript may spend multiple seconds in `recursiveTypeRelatedTo`. This work has been eliminated and should help performance.
38 changes: 0 additions & 38 deletions src/__tests__/fixtures/simpleSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ export type simpleSchema = {
Query: {
kind: 'OBJECT';
name: 'Query';
interfaces: never;
fields: {
todos: {
name: 'todos';
args: any;
type: {
kind: 'LIST';
name: null;
Expand All @@ -24,7 +22,6 @@ export type simpleSchema = {
};
latestTodo: {
name: 'latestTodo';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -37,7 +34,6 @@ export type simpleSchema = {
};
test: {
name: 'test';
args: any;
type: {
kind: 'UNION';
name: 'Search';
Expand Down Expand Up @@ -66,7 +62,6 @@ export type simpleSchema = {
fields: {
id: {
name: 'id';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -79,7 +74,6 @@ export type simpleSchema = {
};
text: {
name: 'text';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -92,7 +86,6 @@ export type simpleSchema = {
};
complete: {
name: 'complete';
args: any;
type: {
kind: 'SCALAR';
name: 'Boolean';
Expand All @@ -101,7 +94,6 @@ export type simpleSchema = {
};
author: {
name: 'author';
args: any;
type: {
kind: 'OBJECT';
name: 'Author';
Expand All @@ -110,15 +102,13 @@ export type simpleSchema = {
};
maxLength: {
name: 'maxLength';
args: any;
type: {
kind: 'SCALAR';
name: 'Int';
ofType: null;
};
};
};
interfaces: 'ITodo';
};

BigTodo: {
Expand All @@ -127,7 +117,6 @@ export type simpleSchema = {
fields: {
id: {
name: 'id';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -140,7 +129,6 @@ export type simpleSchema = {
};
text: {
name: 'text';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -153,7 +141,6 @@ export type simpleSchema = {
};
complete: {
name: 'complete';
args: any;
type: {
kind: 'SCALAR';
name: 'Boolean';
Expand All @@ -162,7 +149,6 @@ export type simpleSchema = {
};
author: {
name: 'author';
args: any;
type: {
kind: 'OBJECT';
name: 'Author';
Expand All @@ -171,26 +157,22 @@ export type simpleSchema = {
};
wallOfText: {
name: 'wallOfText';
args: any;
type: {
kind: 'SCALAR';
name: 'String';
ofType: null;
};
};
};
interfaces: 'ITodo';
};

ITodo: {
kind: 'INTERFACE';
name: 'ITodo';
interfaces: never;
possibleTypes: 'BigTodo' | 'SmallTodo';
fields: {
id: {
name: 'id';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -203,7 +185,6 @@ export type simpleSchema = {
};
text: {
name: 'text';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -216,7 +197,6 @@ export type simpleSchema = {
};
complete: {
name: 'complete';
args: any;
type: {
kind: 'SCALAR';
name: 'Boolean';
Expand All @@ -225,7 +205,6 @@ export type simpleSchema = {
};
author: {
name: 'author';
args: any;
type: {
kind: 'OBJECT';
name: 'Author';
Expand Down Expand Up @@ -274,11 +253,9 @@ export type simpleSchema = {
NoTodosError: {
kind: 'OBJECT';
name: 'NoTodosError';
interfaces: never;
fields: {
message: {
name: 'message';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -295,11 +272,9 @@ export type simpleSchema = {
Todo: {
kind: 'OBJECT';
name: 'Todo';
interfaces: never;
fields: {
id: {
name: 'id';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -312,7 +287,6 @@ export type simpleSchema = {
};
text: {
name: 'text';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -325,7 +299,6 @@ export type simpleSchema = {
};
complete: {
name: 'complete';
args: any;
type: {
kind: 'SCALAR';
name: 'Boolean';
Expand All @@ -334,7 +307,6 @@ export type simpleSchema = {
};
test: {
name: 'test';
args: any;
type: {
kind: 'ENUM';
name: 'test';
Expand All @@ -343,7 +315,6 @@ export type simpleSchema = {
};
author: {
name: 'author';
args: any;
type: {
kind: 'OBJECT';
name: 'Author';
Expand Down Expand Up @@ -376,11 +347,9 @@ export type simpleSchema = {
Author: {
kind: 'OBJECT';
name: 'Author';
interfaces: never;
fields: {
id: {
name: 'id';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -393,7 +362,6 @@ export type simpleSchema = {
};
name: {
name: 'name';
args: any;
type: {
kind: 'NON_NULL';
name: null;
Expand All @@ -406,7 +374,6 @@ export type simpleSchema = {
};
known: {
name: 'known';
args: any;
type: {
kind: 'SCALAR';
name: 'Boolean';
Expand All @@ -419,11 +386,9 @@ export type simpleSchema = {
Mutation: {
kind: 'OBJECT';
name: 'Mutation';
interfaces: never;
fields: {
toggleTodo: {
name: 'toggleTodo';
args: any;
type: {
kind: 'OBJECT';
name: 'Todo';
Expand All @@ -432,7 +397,6 @@ export type simpleSchema = {
};
updateTodo: {
name: 'updateTodo';
args: any;
type: {
kind: 'SCALAR';
name: 'Boolean';
Expand All @@ -445,11 +409,9 @@ export type simpleSchema = {
Subscription: {
kind: 'OBJECT';
name: 'Subscription';
interfaces: never;
fields: {
newTodo: {
name: 'newTodo';
args: any;
type: {
kind: 'OBJECT';
name: 'Todo';
Expand Down
42 changes: 23 additions & 19 deletions src/introspection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,20 @@ interface IntrospectionSchema {
readonly queryType: IntrospectionNamedTypeRef;
readonly mutationType?: IntrospectionNamedTypeRef | null;
readonly subscriptionType?: IntrospectionNamedTypeRef | null;
readonly types: readonly IntrospectionType[];
/* Usually this would be:
| IntrospectionScalarType
| IntrospectionObjectType
| IntrospectionInterfaceType
| IntrospectionUnionType
| IntrospectionEnumType
| IntrospectionInputObjectType;
However, this forces TypeScript to evaluate the type of an
entire introspection query, rather than accept its shape as-is.
So, instead, we constrain it to `any` here.
*/
readonly types: readonly any[];
}

export type IntrospectionType =
| IntrospectionScalarType
| IntrospectionObjectType
| IntrospectionInterfaceType
| IntrospectionUnionType
| IntrospectionEnumType
| IntrospectionInputObjectType;

interface IntrospectionScalarType {
readonly kind: 'SCALAR';
readonly name: string;
Expand All @@ -37,16 +40,20 @@ interface IntrospectionScalarType {
export interface IntrospectionObjectType {
readonly kind: 'OBJECT';
readonly name: string;
readonly fields: readonly IntrospectionField[];
readonly interfaces: readonly IntrospectionNamedTypeRef[] | never;
// Usually this would be `IntrospectionField`.
// However, to save TypeScript some work, instead, we constraint it to `any` here.
readonly fields: readonly any[];
// The `interfaces` field isn't used. It's omitted here
}

interface IntrospectionInterfaceType {
readonly kind: 'INTERFACE';
readonly name: string;
readonly fields: readonly IntrospectionField[];
// Usually this would be `IntrospectionField`.
// However, to save TypeScript some work, instead, we constraint it to `any` here.
readonly fields: readonly any[];
readonly possibleTypes: readonly IntrospectionNamedTypeRef[];
readonly interfaces?: readonly IntrospectionNamedTypeRef[] | null;
// The `interfaces` field isn't used. It's omitted here
}

interface IntrospectionUnionType {
Expand Down Expand Up @@ -92,8 +99,8 @@ export interface IntrospectionNamedTypeRef {

export interface IntrospectionField {
readonly name: string;
readonly args: readonly IntrospectionInputValue[];
readonly type: IntrospectionTypeRef;
// The `args` field isn't used. It's omitted here
}

interface IntrospectionInputValue {
Expand Down Expand Up @@ -132,14 +139,12 @@ type mapField<T> = T extends IntrospectionField
? {
name: T['name'];
type: T['type'];
args: any;
}
: never;

export type mapObject<T extends IntrospectionObjectType> = {
kind: 'OBJECT';
name: T['name'];
interfaces: T['interfaces'][number]['name'];
fields: obj<{
[P in T['fields'][number]['name']]: T['fields'][number] extends infer Field
? Field extends { readonly name: P }
Expand All @@ -158,7 +163,6 @@ export type mapInputObject<T extends IntrospectionInputObjectType> = {
type mapInterface<T extends IntrospectionInterfaceType> = {
kind: 'INTERFACE';
name: T['name'];
interfaces: T['interfaces'] extends readonly any[] ? T['interfaces'][number]['name'] : never;
possibleTypes: T['possibleTypes'][number]['name'];
fields: obj<{
[P in T['fields'][number]['name']]: T['fields'][number] extends infer Field
Expand Down Expand Up @@ -224,8 +228,8 @@ export type ScalarsLike = {

export type IntrospectionLikeType = {
query: string;
mutation: string | never;
subscription: string | never;
mutation?: any;
subscription?: any;
types: { [name: string]: any };
};

Expand Down

0 comments on commit d383684

Please sign in to comment.