GithubHelp home page GithubHelp logo

RFC: Graphcache strong typing about urql HOT 3 OPEN

BickelLukas avatar BickelLukas commented on June 3, 2024
RFC: Graphcache strong typing

from urql.

Comments (3)

BickelLukas avatar BickelLukas commented on June 3, 2024 1

Including it in gql.tada would definitely be a step in the right direction.
But I was thinking since graphcache already supports a schma aware mode, this could be implemented with any schema that is supplied to graphcache, regardless of where it comes from.
This would decouple it completely from gql.tata and make it compatible with other graphql type generators

from urql.

kitten avatar kitten commented on June 3, 2024

This is definitely something we intend to include in gql.tada. The only issue I can foresee is that the introspection output there isn't 100% stabilised, and we plan to actually output the pre-formatted version of it as well.

So, if we write up a version that's targeting the current output in gql.tada (from mapIntrospection) that'd likely help with performance of inference in the future.

There's also some old issues to revisit with the typings and how they're being consumed by Graphcache, but that's likely TBD until we explore this again.

But yea, I'd currently favour absorbing the complexity here into a gql.tada sub-module (under maybe gql.tada/addons/graphcache maybe)
cc @JoviDeCroock

from urql.

brazelja avatar brazelja commented on June 3, 2024

I figured I'd add what I've been patching into my current project as it might be useful in further implementing this feature.

When using Cache.resolve, I noticed that sometimes a scalar value is returned and other times a reference is returned if the result would have been an object due to the cache normalizing data. It also seemed like it would be nice if there was type-aware autocompletion regarding the field parameter of the resolve function as well, so I came up with the following:

Cache Normalization

import type { DataField, Entity, FieldArgs } from '@urql/exchange-graphcache';

/**
 * This utility type represents a reference returned from `Cache.resolve`,
 * typically looking like `Book:123` but with the full type being referenced
 * stored alongside it in branded-type fashion
 */
type Reference<T> = string & { __def: T };

/**
 * This utility type is used to "normalize" an object type and return it
 * as either a reference type or a scalar.
 */
type Normalize<T> =
  T extends Array<infer U>
    ? Array<Normalize<U>>
    : T extends object // Check if T can be normalized
      ? Reference<T> // If it can, return the normalized reference type
      : T; // If it doesn't, return T

// Example
type Book = {
  __typename: 'Book';
  id: string;
  title: string;
  publicationDate: string;
  author: {
    __typename: 'Author';
    id: string;
    name: string;
  };
};
type BookReference = Reference<Book>;
//   ▲ string & { __def: Book; }

Helpers

/**
 * This helper casts any `Entity` to a reference type, essentially prepping it
 * for use in `Cache.resolve` without actually modifying its underlying type.
 */
const toReference = <T>(entity: Entity): Reference<T> => entity as Reference<T>;

/**
 * This utility type is used to resolve a reference type to its full type
 */
type Resolved<T extends Reference<any>> = T extends Reference<infer U> ? U : T;

@urql/exchange-graphcache Type Override

By overriding the definition of the Cache.resolve function, I am able to
hook into the type E of the entity parameter, which then gives me access to the underlying type being obfuscated by Reference. This can then be leveraged to add autocompletion of the field parameter by limiting it to properties that exist on the resolved type of E. The return type will be the normalized value of the property accessed via F, which will either be a scalar or another Reference.

declare module '@urql/exchange-graphcache' {
  interface Cache {
    // Custom typing
    resolve<E extends Reference<any>, F extends keyof Resolved<E>>(
      entity: E | undefined,
      field: F, // You get type completion here
      args?: FieldArgs,
    ): Normalize<Resolved<E>[F]>;

    // Original typing
    resolve(entity: Entity | undefined, fieldName: string, args?: FieldArgs): DataField | undefined;
  }
}

Usage

Within an optimistic update:

type Book = {
  __typename: 'Book';
  id: string;
  title: string;
  publicationDate: string;
  author: {
    __typename: 'Author';
    id: string;
    name: string;
  };
};

const cache = cacheExchange({
  optimistic: {
    updateBook: (args, cache) => {
      const bookRef = toReference<Book>({ __typename: 'Book', id: args.id });
      //    ▲ Reference<Book>

      return {
        __typename: 'Book',
        id: args.id,
        title: args.title,
        author: () => {
          //                                       ▼ intellisense limits this to keyof Book
          const authorRef = cache.resolve(bookRef, 'author');
          //    ▲ Reference<{
          //         __typename: 'Author';
          //        id: string;
          //        name: string;
          //      }>

          return {
            __typename: 'Author',
            //                           ▼ intellisense limits this to '__typename' | 'id' | 'name'
            id: cache.resolve(authorRef, 'id'), // string
            //                                   ▼ intellisense limits this to '__typename' | 'id' | 'name'
            name: () => cache.resolve(authorRef, 'name'), // string
          };
        },
      };
    },
  },
});

from urql.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.