prisma-nestjs-graphql icon indicating copy to clipboard operation
prisma-nestjs-graphql copied to clipboard

GroupByArgs not compatible with groupBy prisma method

Open lukadriel7 opened this issue 4 years ago • 5 comments
trafficstars

Hello, I think I found an issue, unless it works as expected. I usually directly use the graphql args in the prisma function with no issue. I decided to test the groupBy method but I find some issues when I try to use the GroupByArgs type const as parameter but I got errors about circular references

This is the code I used

@Mutation(() => User)
  async createUser(
    @Args() params: CreateOneUserArgs,
    @Info() info: GraphQLResolveInfo,
  ) {
    const group: UserGroupByArgs = {
      by: [UserScalarFieldEnum.name],
    };

    this.prismaService.user.groupBy(group);
    const select = new PrismaSelect(info).value;
    params = { ...params, ...select };
    return this.prismaService.user.create(params);
  }

and these are the errors printing (They also show up in the editor) .

src/users/users.resolver.ts:37:5 - error TS2615: Type of property 'AND' circularly references itself in mapped type '{ [K in keyof { AND?: UserScalarWhereWithAggregatesInput[]; OR?: UserScalarWhereWithAggregatesInput[]; NOT?: UserScalarWhereWithAggregatesInput[]; id?: IntWithAggregatesFilter; name?: StringWithAggregatesFilter; profile?: JsonWithAggregatesFilter; }]: Or<...> extends 1 ? { ...; }[K] extends infer TK ? GetHavingField...'.

37     this.prismaService.user.groupBy(group);
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/users/users.resolver.ts:37:5 - error TS2615: Type of property 'NOT' circularly references itself in mapped type '{ [K in keyof { AND?: UserScalarWhereWithAggregatesInput[]; OR?: UserScalarWhereWithAggregatesInput[]; NOT?: UserScalarWhereWithAggregatesInput[]; id?: IntWithAggregatesFilter; name?: StringWithAggregatesFilter; profile?: JsonWithAggregatesFilter; }]: Or<...> extends 1 ? { ...; }[K] extends infer TK ? GetHavingField...'.

37     this.prismaService.user.groupBy(group);
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/users/users.resolver.ts:37:5 - error TS2615: Type of property 'OR' circularly references itself in mapped type '{ [K in keyof { AND?: UserScalarWhereWithAggregatesInput[]; OR?: UserScalarWhereWithAggregatesInput[]; NOT?: UserScalarWhereWithAggregatesInput[]; id?: IntWithAggregatesFilter; name?: StringWithAggregatesFilter; profile?: JsonWithAggregatesFilter; }]: Or<...> extends 1 ? { ...; }[K] extends infer TK ? GetHavingField...'.

37     this.prismaService.user.groupBy(group);
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/users/users.resolver.ts:37:37 - error TS2345: Argument of type 'UserGroupByArgs' is not assignable to parameter of type '{ where?: UserWhereInput; orderBy?: UserOrderByWithAggregationInput[]; by: UserScalarFieldEnum[]; having?: UserScalarWhereWithAggregatesInput; ... 6 more ...; _max?: UserMaxAggregateInput; } & { ...; } & (`Error: Field "${any}" used in "having" needs to be provided in "by".` | [...])'.
  Type 'UserGroupByArgs' is not assignable to type '{ where?: UserWhereInput; orderBy?: UserOrderByWithAggregationInput[]; by: UserScalarFieldEnum[]; having?: UserScalarWhereWithAggregatesInput; ... 6 more ...; _max?: UserMaxAggregateInput; } & { ...; } & [...]'.
    Type 'UserGroupByArgs' is not assignable to type '{ orderBy: Enumerable<UserOrderByWithAggregationInput>; }'.
      Property 'orderBy' is optional in type 'UserGroupByArgs' but required in type '{ orderBy: Enumerable<UserOrderByWithAggregationInput>; }'.

37     this.prismaService.user.groupBy(group);
                                       ~~~~~

[00:22:13] Found 4 errors. Watching for file changes.

Environment

"@prisma/client": "^2.23.0", "prisma": "^2.23.0", "prisma-nestjs-graphql": "^12.0.0", "@nestjs/common": "^7.6.17", "@nestjs/core": "^7.6.17", "@nestjs/graphql": "^7.10.6",

lukadriel7 avatar May 20 '21 00:05 lukadriel7

They are compatible in terms of assignment:

{
    const x: UserGroupByArgs = {
        by: [UserScalarFieldEnum.id],
    };
    let p: Prisma.UserGroupByArgs = {
        by: [UserScalarFieldEnum.id],
    };
    p = x; // No Errors
    // $prisma.user.groupBy(x); // Error
    // $prisma.user.groupBy(p); // Error
}

groupBy method has weird signature

    groupBy<
      T extends UserGroupByArgs,
      HasSelectOrTake extends Or<
        Extends<'skip', Keys<T>>,
        Extends<'take', Keys<T>>
      >,
      OrderByArg extends True extends HasSelectOrTake
        ? { orderBy: UserGroupByArgs['orderBy'] }
        : { orderBy?: UserGroupByArgs['orderBy'] },
      OrderFields extends ExcludeUnderscoreKeys<Keys<MaybeTupleToUnion<T['orderBy']>>>,
      ByFields extends TupleToUnion<T['by']>,
      ByValid extends Has<ByFields, OrderFields>,
      HavingFields extends GetHavingFields<T['having']>,
      HavingValid extends Has<ByFields, HavingFields>,
      ByEmpty extends T['by'] extends never[] ? True : False,
      InputErrors extends ByEmpty extends True
      ? `Error: "by" must not be empty.`
      : HavingValid extends False
      ? {
          [P in HavingFields]: P extends ByFields
            ? never
            : P extends string
            ? `Error: Field "${P}" used in "having" needs to be provided in "by".`
            : [
                Error,
                'Field ',
                P,
                ` in "having" needs to be provided in "by"`,
              ]
        }[HavingFields]
      : 'take' extends Keys<T>
      ? 'orderBy' extends Keys<T>
        ? ByValid extends True
          ? {}
          : {
              [P in OrderFields]: P extends ByFields
                ? never
                : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
            }[OrderFields]
        : 'Error: If you provide "take", you also need to provide "orderBy"'
      : 'skip' extends Keys<T>
      ? 'orderBy' extends Keys<T>
        ? ByValid extends True
          ? {}
          : {
              [P in OrderFields]: P extends ByFields
                ? never
                : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
            }[OrderFields]
        : 'Error: If you provide "skip", you also need to provide "orderBy"'
      : ByValid extends True
      ? {}
      : {
          [P in OrderFields]: P extends ByFields
            ? never
            : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`
        }[OrderFields]
    >(args: SubsetIntersection<T, UserGroupByArgs, OrderByArg> & InputErrors): {} extends InputErrors ? GetUserGroupByPayload<T> : Promise<InputErrors>

And as showed above does not compatible with types by prisma

const p: Prisma.UserGroupByArgs = {
	by: ['id'],
};
$prisma.user.groupBy(p); // Error

Looks like a bug of prisma

unlight avatar May 20 '21 12:05 unlight

Thanks, I will try to create an issue on prisma side and pray for the best

lukadriel7 avatar May 20 '21 15:05 lukadriel7

I was going to create also, If you create, drop here a link

unlight avatar May 20 '21 15:05 unlight

@unlight here prisma/prisma#7183

lukadriel7 avatar May 20 '21 16:05 lukadriel7

Workaround is obvious: disable type check by @ts-ignore or by converting to any type, or make explicit type coercion:

type UserGroupBy = Parameters<typeof $prisma.user.groupBy>[0];
const x: UserGroupByArgs = {
    by: [UserScalarFieldEnum.id],
};
let p: Prisma.UserGroupByArgs = {
    by: [UserScalarFieldEnum.id],
};

$prisma.user.groupBy(p as GroupBy); // No Error
$prisma.user.groupBy(x as GroupBy); // No Error

unlight avatar Nov 06 '21 17:11 unlight