Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript GraphQLObjectType and TArgs #2152

Closed
williambailey opened this issue Sep 3, 2019 · 10 comments · Fixed by #3328
Closed

TypeScript GraphQLObjectType and TArgs #2152

williambailey opened this issue Sep 3, 2019 · 10 comments · Fixed by #3328

Comments

@williambailey
Copy link

The TypeScript definitions currently specify a TArgs generic type on GraphQLObjectType which then flows into GraphQLObjectTypeConfig, GraphQLFieldConfigMap and GraphQLFieldMap.

All field resolver arguments on an object therefore have to have the same base type.

This, to me, is unexpected as each resolve function can have it's own differing requirements as to what arguments and it makes it awkward to use types arguments as you need to specify something like any when creating the GraphQLObjectType instance. For example:

interface MySource { /* ... */ }
interface MyContext { /* ... */ }

interface MyFieldArgs {
    foo?: number;
    bar: string;
}

interface MyOtherFieldArgs {
    baz: string;
}

const MyType = new GraphQLObjectType<MySource, MyContext, any>({
    fields: {
        myField: {
            args: {
                bar: { type: new GraphQLNonNull(GraphQLString) },
                foo: { type: GraphQLInt },
            },
            resolve: (source, args: MyFieldArgs, context, info) => { /* ... */ },
            type: GraphQLString,
        },
        myOtherField: {
            args: {
                baz: { type: GraphQLString },
            },
            resolve: (source, args: MyOtherFieldArgs, context, info) => { /* ... */ },
            type: GraphQLString,
        },        
    },
    name: "My",
});

I suppose you could stick with the default TArgs type and then use a typescript user-defined type guard. However this is even more awkward and seems unnecessary given that we are specifying the arguments in the GraphQLFieldConfig and can be sure that graphql has already validated and asserted that the arguments match what we expect when it comes to calling the resolve function.

function isMyFieldArgs(o: any): o is MyFieldArgs {
    /* check everything ... */
    return true;
}

{
    // ...
    resolve: (source, args, context, info) => {
        if (!isMyFieldArgs(args)) {
            return undefined;
        }
        /* ... */
    },
}
@williambailey
Copy link
Author

This was also raised in DefinitelyTyped/DefinitelyTyped#21359

@IvanGoncharov
Copy link
Member

@williambailey Thanks for the detailed report 👍
We don't plan to change TS typings (except bug fixes) in 14.x.x release line.
But we are working on full TS conversion as part of 15.x.x (ETA end of this month).

@icepeng
Copy link

icepeng commented Nov 19, 2019

+1

@mike-marcacci
Copy link
Contributor

To update this issue, it's my understanding that v15 (which is about to have a release candidate) will largely retain the existing types imported from DefinitelyTyped, and will thus include this flaw. In the following months, this codebase is to be rewritten in TypeScript (see #2104) which will hopefully include a complete redesign of the types (see #2188) that will fix this while also inferring resolver types based on GraphQL types.

@mike-marcacci
Copy link
Contributor

This specific issue was solved in #2488 so this issue can probably be closed.

@williambailey
Copy link
Author

williambailey commented Nov 5, 2020

Having just gone through the migration to v15 this is still an issue and arguebly worse since #2488.

Now we have GraphQLObjectType<TSource = any, TContext = any> so we can't specify TArgs as any at the top level and have it propagate to the children which default to TArgs = { [key: string]: any }.

This then leads to the situation where your typescript failes to build when you try to have infered and typed TArgs in your resolvers with errors such as:

Type '{ [argName: string]: any; }' is missing the following properties from type 'MyImageRequestArgs': height, width ts(2322)
definition.d.ts(447, 3): The expected type comes from property 'fields' which is declared here on type 'Readonly<GraphQLObjectTypeConfig<MySourceObject, MyContextValue>>'

MyImageRequestArgs in this instance would look something like

interface MyImageRequestArgs {
    [argName: string]: any;
    height: number;
    width: number;
}

Our workaround at the moment is to patch the GraphQLFieldConfig definition with something like

--- a/node_modules/graphql/type/definition.d.ts
+++ b/node_modules/graphql/type/definition.d.ts
@@ -511,7 +511,7 @@
 export interface GraphQLFieldConfig<
   TSource,
   TContext,
-  TArgs = { [argName: string]: any }
+  TArgs = any
 > {
   description?: Maybe<string>;
   type: GraphQLOutputType;

I will continue to see if there is something else going on that i'm missing but thought i'd give a quick update to the ticket.

@melashkov
Copy link

Looks like someone else aready tried to fix it same way, #2518 just linking the PR

@dmastrorillo
Copy link

Do we have an update on this? I using version 15.5.0 and still facing the aforementioned issue

Type 'GraphQLFieldResolver<undefined, Context, MutationSignupArgs>' is not assignable to type 'GraphQLFieldResolver<undefined, Context, { [argName: string]: any; }>'. Type '{ [argName: string]: any; }' is missing the following properties from type 'MutationSignupArgs': firstName, lastName, email

@dmitryvmin
Copy link

Bump. Same issue.

@mabn
Copy link

mabn commented Oct 21, 2021

Similar to #2829

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants