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

RFC: Add stricter types for cache in graphcache resolvers #2558

Open
nandorojo opened this issue Jul 22, 2022 · 6 comments
Open

RFC: Add stricter types for cache in graphcache resolvers #2558

nandorojo opened this issue Jul 22, 2022 · 6 comments
Labels
future 🔮 An enhancement or feature proposal that will be addressed after the next release

Comments

@nandorojo
Copy link
Sponsor Contributor

nandorojo commented Jul 22, 2022

Summary

Currently, cache.invalidate('Query', fieldName, field.args) lets you pass any arbitrary string to fieldName and object to the third argument. This means you have to manually cast types beforehand.

Proposed Solution

urql.mp4

It would be nice if the cache accepted an optional Query generic to give you autocomplete suggestions. For example, if you have myBookings inside of Query, then cache.invalidate('Query', would auto-suggest myBookings for the fieldName, and perhaps it could autocomplete the args too.

All of it could be easily made optional with things like keyof Query | (string & {}) for backwards compatibility.

Requirements

I haven't looked into the cache exchange's internals yet, but presumably this would require passing the Query generic to the cache. Assuming the generic can get passed down, this should work with urql-graphcache's codegen. I'm happy to look into making a PR if this sounds like something you'd approve.

Thanks!

@nandorojo nandorojo added the future 🔮 An enhancement or feature proposal that will be addressed after the next release label Jul 22, 2022
@JoviDeCroock
Copy link
Collaborator

I have been thinking about this, and it might entail a lot of complexity to get all these types in there correctly, tried making a little start here but I am not sure if TypeScript allows us to narrow down the types based on the arguments we are putting in, i.e. we input Query as the first argument now it narrows down what can exist in the second argument and after whether or not arguments are needed.

I think we could adopt this by expanding GraphQL-Code-Generator to pass a generic to the Resolver/... type

@prichodko
Copy link

prichodko commented Jul 27, 2022

@JoviDeCroock it should be possible. I've updated your TypeScript playground here.

With conditional type constraints it's also possible to either require or not allow passing the third argument.

Happy to help out if necessary. 👍

@nandorojo
Copy link
Sponsor Contributor Author

Thanks @prichodko!

We can indeed narrow down the types based on inputs using genetics. I’ve done this a few times so I’m happy to help too.

With conditional type constraints it's also possible to either require or not allow passing the third argument.

This should work. TypeScript overloads may be an option too, but I think conditional types + an arguments array that gets spread should work.

@JoviDeCroock
Copy link
Collaborator

With that method it should probably be possible to assemble a similar object defining the types with their fields and arguments in the GraphQL code generator plugin and then passing that on to our Store which can use it to statically type invalidate/resolve/... I think the bigger issue presents itself for the sub-types where we need to support a key for instance Todo:123 and their object like variant { id: 123, __typename: 'Todo' } I think that was the main thing stopping me when initially implementing the code generation plugin 😅

@prichodko
Copy link

Would something like this help?

@nandorojo
Copy link
Sponsor Contributor Author

One simple piece that is missing right now:

cache.invalidate({ __typename })

In this case, __typename should be restricted to the available types. That could be inferred from the Query type, by reading 1) the values of Query (Query[keyof Query]) and extracting the object types:

type ExtractHasTypename<T> = T extends { __typename: string } ? T : never

type QueryValue = Query[keyof Query]

type Typename = ExtractHasTypename<QueryValue>['__typename']

There, Typename will give us all the available __typename values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
future 🔮 An enhancement or feature proposal that will be addressed after the next release
Projects
None yet
Development

No branches or pull requests

3 participants