-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Investigate using TypeScript strict mode with Redwood #5481
Comments
Just bumped my app to canary and there still seems to be issues on cross calling resolvers which is kind of intrinsic to how GraphQL resolvers work, I'll explain via an example. This mutation returns a legit slug or empty string to imply you can't use it: export const canUseHandle: MutationResolvers["canUseHandle"] = async ({ handle }) => {
const invalid = leo.check(handle)
if (invalid) return ""
const handleSlug = slugify(handle)
const exists = await db.user.findUnique({ where: { username: handleSlug } })
if (exists) return ""
return handleSlug
} Which looks like this to the type system: const canUseHandle: (args: RequireFields<MutationcanUseHandleArgs, "handle">, obj: {
root: {};
context: RedwoodGraphQLContext;
info: GraphQLResolveInfo;
}) => Partial<...> | Promise<...> The tricky bit is here export const createNewUserOnAccount: MutationResolvers["createNewUserOnAccount"] = async (args) => {
const username = await canUseHandle({ handle: args.input.username }, {} as any)
if (!username) {
return null
}
// .... Because the type of Looking at the codegen code - I think I would have expected to see a lot of |
I'm also super stuck with this problem... I don't understand why TypeScript in strict mode doesn't let me await a If you modify the |
There's a very very slim possibility of a TS bug here, this simplified model of the issue doesn't have issues when doing Here's a very close copy which does not repro Here's a full failing repro - changing Yeah, after digging in, I'm not convinced this is a bug - I think this behavior from TS is correct. Just because I declared that const canUseHandle: MutationResolversFn["canUseHandle"] = async ({ handle }) => {
return handle
} |
Think I follow what you mean... but what I still dont understand is why removing |
I think there might be an issue with the type generation here. type CanUseHandle = ({ handle }: { handle: string }) => Promise<Partial<Promise<string> | string>> But I think it should be type CanUseHandle = ({ handle }: { handle: string }) => Promise<Partial<string>> | Partial<string> Having |
Oh I think I understand. Some other wrapper there not in the ResolverFn itself - gotcha, great find. |
So looks like we have a fix based on @Tobbe's observation. Since our types weren't complex enough, we made it a little more complex: export type ResolverFn<TResult, TParent, TContext, TArgs> = (
args: TArgs,
obj?: { root: TParent; context: TContext; info: GraphQLResolveInfo }
) => TResult extends PromiseLike<TResult>
? Promise<Partial<Awaited<TResult>>>
: Promise<Partial<TResult>> | Partial<TResult> @orta thoughts? |
Looking at the actual RW source code it's really So, If we branch on it being a I think the end result would be this export type ResolverFn<TResult, TParent, TContext, TArgs> = (
args: TArgs,
obj?: { root: TParent; context: TContext; info: GraphQLResolveInfo }
) => TResult extends PromiseLike<unknown>
? Promise<Promise<Partial<Awaited<TResult>>>> | Promise<Partial<Awaited<TResult>>>
: Promise<Partial<TResult>> | Partial<TResult> |
Tricky, running either of these doesn't improve too much, the big two off the bar are:
export const game: QueryResolvers["game"] = ({ id }) =>
const query = id.length > 10 ? { id } : { slug: id }
return db.game.findUnique({ where: query })
}
Type 'Game | null' is not assignable to type 'Partial<Promise<Game>>'.
Type 'null' is not assignable to type 'Partial<Promise<Game>>'.ts(2322) Now fails because the only acceptable responses are I think I'm increasingly feeling like the answer doesn't live inside configuring graphql-codegen with a more and more complex type but in creating per-service file type definition which are explicitly generating the exact types for the exports of that file, and maybe even allowing for the return types to explicitly be Prisma objects so that you don't have the |
The problem you're seeing here, I believe is something with how typescript in strict mode interprets I'm waiting to hear back from the Prisma team - this seems to be the last unresolved issue on the API side - it happens exclusively on the
The reason I prefer gql-codegen still, is because if, for example, you use "select" in your Prisma queries, without using the codegen types, we wouldn't know what fields are required. e.g. // This will error out with gql-codegen, telling us we need to return atleast whatever the sdl defines bazingas as
const bazingas: QueryResolvers['bazingas'] = () => {
return db.bazinga.findMany({select: {id: true}}
}
|
Hey @orta - I'm wondering if I could get your help on the last remaining issue? To summarise the problem:
This means that you can't just return from a
Here's a playground repro of the problem. I understand that the types aren't exactly the same, but in reality this really shouldn't be an issue because at runtime there's no difference between the two types. For now I'm just going to document it, but if you had any tips or suggestions I'm all ears - I've bothered a lot of people about this, but can't seem to find a clear solution. |
@dac09 Not sure if this helps for the |
I asked in the discord, where we got some pretty good explanations about why structurally you can only really compare in one direction: Which I think makes sense generally, what you accept for one of those - personally, I think switching graphql-codegen to use |
Things to investigate:
Resolvers
Following #5437 we make it hard to use Resolvers with TS strict mode, because for convenience of testing we set obj and args as optional (which is technically not correct).
When strict mode is ON, we can switch to using the complete definition of
ResolverFn
Docs on:
The text was updated successfully, but these errors were encountered: