Right now, the cache exchange's types seem to do a good job at preventing any type errors without manual casting. That being said, it doesn't have the same support for type-hinting that other parts of the package (e.g., useQuery) do. This also seems to have confused some people (e.g., #851). I think the cacheExchange could provide better type support by being transformed into a generic function that lets users provide the types that should appear in their cache, so that, e.g., when writing an optimistic response, the types of arguments available will be listed.
I can think of two ways to achieve this. First, provide manual overrides of the CacheExchangeOpts types. This would enable graphql code generating tools to build these types. However, this makes it highly unlikely that someone would build all these types by hand, since that'd be just as slow as using the existing types. And, until the code generators tools exist, this feature would be mostly useless.
The second way would be to let something representing the schema be passed to it, and determine the types that way. I think the best candidate would be something like graphql-codegen's typescript resolvers. Then, the cache exchange could derive the types it needs from that. I built out types that at least show it's possible to do this for all of the fields on the cache exchange options – I'm happy to post all of those if that's helpful, but to give an example of just one:
type ResolverMap = Record<string, Record<string, (...args) => any>>; // Simplified representation of graphql codegen's resolver type
type MapParent<T extends Record<string, AnyFunction>> = {
[P in keyof T]: ReturnType<T[P]>
}
type NewResolverConfig<T extends ResolverMap> = {
[P in keyof T]: {
[R in keyof T[P]]: (parent: MapParent<T[P]>, args: Parameters<T[P][R]>[1], cache: Cache, info: ResolverInfo) => DataField | undefined; // The return type could likely be improved – maybe Partial<ReturnType<T[P][R]>> | undefined
}
}
export interface CacheExchangeOpts<T extends ResolverMap | undefined = undefined> {
resolvers?: T extends undefined ? ResolverConfig : NewResolverConfig<T>;
// ... updates, optimistic, etc.
}
Then, a user would be able to define a type representing their schema and pass it to cache exchange to get type suggestions:
type SchemaResolver = {
UserDefinedType: {
field: (parent: UserDefinedType, args: { id: string }) => DataField
}
}
const myCacheExchange = cacheExchange<SchemaResolver>({ ... });
// Or, of course
const cacheOptions: CacheExchangeOpts<SchemaResolver> = {
...
}
const myCacheExchange = cacheExchange(cacheOptions);
_(Side note – this is my first time contributing to this repo, using a broad definition of the word "contribute", so if there's something about this issue I should change to make it more useful, let me know and I'm happy to do so)_
We've been really confident that a solution to this can be found, which can likely be added exactly as you're suggesting.
The main problem is that we haven't found a way yet to generate TypeScript typings for an entire schema using GraphQL Code Generator. (then again, I personally haven't looked too closely yet)
It's possible that a GraphQL Code Generator plugin can be written to generate a complete type set that would make it possible to type each possible field here, and I'd love to find out more about this! Since we have a lot of access in GraphQL Code Generator to read the schema, as far as I know, it's possible to write a custom plugin for this, but I'm interested in whether there's some prior art on the project.
It's also possible that we'd first have to internalise these plugins, which would be a lot of maintenance work for us, as we haven't yet built up an efficient line of communication with the GCG maintainers, see e.g. https://github.com/dotansimha/graphql-code-generator/pull/3648
@kitten Thank you for the quick response. I'm working on a couple of GCG plugins now, and based on that, I will say a) it's a very well designed system, so it would definitely be possible to make something to do this, but b) it may not be super easy. Since you'll have different types of output for each field (resolvers, updates, etc.), you may need to write a few plugin's worth of code to achieve it. And internalizing it may be tough, but the core library is on npm, so I'd think that would be achievable.
But the issues with developing, effectively, "multiple plugins" made me think morphing the shape of the data in typescript would be simpler than a code generator. More generally, though, this would be really useful for me to have, and if there's any way to help, I'm more than happy to, no matter what approach you take to the problem.
@kitten I really hope we can improve our communication :) Sorry for neglecting https://github.com/dotansimha/graphql-code-generator/pull/3648, hope to get it in soon ;)
@dotansimha no worries! I believe @bkonkle is slowly working on type generation for the Graphcache config object. That'll come along rather slowly so don't expect updates too soon 😅 but once that's done I'll ping you so we can have a think about where it should live ✌️
Most helpful comment
@dotansimha no worries! I believe @bkonkle is slowly working on type generation for the Graphcache config object. That'll come along rather slowly so don't expect updates too soon 😅 but once that's done I'll ping you so we can have a think about where it should live ✌️