mercurius-js / mercurius-typescript

TypeScript usage examples and "mercurius-codegen" for Mercurius

Home Page:https://mercurius.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to use generic arguments on withFilter in Subscriptions

DevelopmentByDavid opened this issue · comments

TLDR

Typing issue when using generics on the withFilter function supplied by the mercurius package on subscriptions.

Problem

Given the following code

type PayloadType = { eventId: string };
export const resolvers =  {
    Subscription: {
        likeCountChanged: {
            subscribe: withFilter<PayloadType>(
                (parent, args, ctx) => ctx.pubsub.subscribe('likeCountChanged'),
                (payload, args, ctx) => Question.doesEventMatch(args.eventId, payload)
            ),
        },
    },
}

I get the following error

Type '{ subscribe: (root: any, args: Record<string, any>, context: MercuriusContext, info: GraphQLResolveInfo & { mergeInfo?: MergeInfo | undefined; }) => AsyncGenerator<...>; }' is not assignable to type 'SubscriptionResolver<Maybe<ResolverTypeWrapper<EventQuestion>>, "likeCountChanged", {}, MercuriusContext, RequireFields<SubscriptionlikeCountChangedArgs, "eventId">> | undefined'.
  Property 'resolve' is missing in type '{ subscribe: (root: any, args: Record<string, any>, context: MercuriusContext, info: GraphQLResolveInfo & { mergeInfo?: MergeInfo | undefined; }) => AsyncGenerator<...>; }' but required in type 'SubscriptionResolverObject<Maybe<ResolverTypeWrapper<EventQuestion>>, {}, MercuriusContext, RequireFields<SubscriptionlikeCountChangedArgs, "eventId">>'.ts(2322)
graphql-types.ts(439, 5): 'resolve' is declared here.

My relevant generated code looks the same as shown in the examples folder here:

export interface SubscriptionSubscriberObject<
TResult,
TKey extends string,
TParent,
TContext,
TArgs
> {
subscribe: SubscriptionSubscribeFn<
{ [key in TKey]: TResult },
TParent,
TContext,
TArgs
>
resolve?: SubscriptionResolveFn<
TResult,
{ [key in TKey]: TResult },
TContext,
TArgs
>
}
export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>
resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>
}
export type SubscriptionObject<
TResult,
TKey extends string,
TParent,
TContext,
TArgs
> =
| SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
| SubscriptionResolverObject<TResult, TParent, TContext, TArgs>
export type SubscriptionResolver<
TResult,
TKey extends string,
TParent = {},
TContext = {},
TArgs = {}
> =
| ((
...args: any[]
) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
| SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>

Solutions

  1. Adding a resolve field that returns null
type PayloadType = { eventId: string };
export const resolvers =  {
    Subscription: {
        likeCountChanged: {
            subscribe: withFilter<PayloadType>(
                (parent, args, ctx) => ctx.pubsub.subscribe('likeCountChanged'),
                (payload, args, ctx) => Question.doesEventMatch(args.eventId, payload)
            ),
           resolve: () => null
        },
    },
}
  1. Not using the generic also gives no errors
// no errors with the below code if I remove the generic argument
export const resolvers =  {
    Subscription: {
        likeCountChanged: {
            subscribe: withFilter(
                (parent, args, ctx) => ctx.pubsub.subscribe('likeCountChanged'),
                (payload, args, ctx) => Question.doesEventMatch(args.eventId, payload)
            ),
        },
    },
}

Also, I'm not sure if I should be asking this question here, in the graphql-codegen package, or in mecurius repo, but since this possibly has to do with the generated code then I thought I'd ask here first.

From my understanding, I should not need to use the resolve field. If I need to, then I must have missed something in https://github.com/mercurius-js/mercurius/blob/master/docs/subscriptions.md#subscription-filters

@DevelopmentByDavid I just took a look at this issue and it seems like this issue appears when the generic you specify doesn't match the generated type, that's how I could reproduce it, but that's not an issue with this package of course, could you confirm if making sure the types match solves this issue? remember that the type is not the field, but an object with the field with it's correct name, for example, withFilter<{ likeCountChanged: number }> and not withFilter<number>

@PabloSzx You are correct! Thank you! I did not realize that there needed to be a nested field with the same name as the subscription itself.

Where is the above requirement documented? Is this something to do with graphql.js itself? If this is mercurius specific I'd be happy to open a PR to add the documentation necessary to the subscriptions section.

@PabloSzx You are correct! Thank you! I did not realize that there needed to be a nested field with the same name as the subscription itself.

Where is the above requirement documented? Is this something to do with graphql.js itself? If this is mercurius specific I'd be happy to open a PR to add the documentation necessary to the subscriptions section.

This is from the graphql.js implementation, and if you think about it, it makes sense, since it possible that from a single pubsub publish to trigger multiple different subscriptions

I see. I'm still a bit new to graphql and learning. Thank you for letting me know and being so responsive! Apologies for the extra work this may have caused you.