Typescript and Apollo Client return types intertwined

My goal is to create a simple function within a class that generates an Apollo Client. Below is the code I have implemented:

import appConfig from 'config/app-config';
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import LocalStorageKeys from 'constants/local-storage-keys';
import { setContext } from '@apollo/client/link/context';

export class ApolloClientServiceImpl {
  private cache: InMemoryCache;
  constructor() {
    this.cache = new InMemoryCache();
  }

  createApolloClient(idToken: string): unknown {
    const httpLink = createHttpLink({
      uri: appConfig.hasura.url,
    });
    const authLink = setContext((_, { headers }) => {
      let bearerToken = localStorage.getItem(LocalStorageKeys.TOKEN);
      if (idToken) {
        bearerToken = idToken;
      }
      return {
        headers: {
          ...headers,
          authorization: bearerToken ? `Bearer ${bearerToken}` : '',
        },
      };
    });
    return new ApolloClient({
      link: authLink.concat(httpLink),
      cache: this.cache,
    });
  }
}

The issue arises with the return type of the createApolloClient function. If I specify it as

ApolloClient<InMemoryCache>
and use the following return statement:

return new ApolloClient<InMemoryCache>({
      link: authLink.concat(httpLink),
      cache: this.cache,
    });

This results in the error:

Type 'InMemoryCache' is not assignable to type 'ApolloCache<InMemoryCache>'.
  Types of property 'restore' are incompatible.
    Type '(data: NormalizedCacheObject) => InMemoryCache' is not assignable to type '(serializedState: InMemoryCache) => ApolloCache<InMemoryCache>'.
      Types of parameters 'data' and 'serializedState' are incompatible.
        Type 'InMemoryCache' is not assignable to type 'NormalizedCacheObject'.
          Index signature is missing in type 'InMemoryCache'.ts(2322)

Given the limited documentation on this topic in the Apollo client docs, my question is, what is the correct return type for this function?

EDIT: By sticking with just:

    return new ApolloClient({
      link: authLink.concat(httpLink),
      cache: this.cache,
    });

And changing the return type to

ApolloClient<NormalizedCacheObject>
, this resolves the error. Many thanks to Alluan Hadad.

Answer №1

Explore my course where I delve into the specifics of working with

ApolloClient<NormalizedCacheObject>

import { InMemoryCache, NormalizedCacheObject } from "apollo-cache-inmemory";
import { ApolloClient } from "apollo-client";
import { FragmentMatcher } from "apollo-client/core/LocalState";
import { ApolloLink } from "apollo-link";
import { onError } from "apollo-link-error";
import { HttpLink } from "apollo-link-http";

enum EApolloCLientCredentials {
    DEFAULT_OMIT = "omit",
    INCLUDE = "include",
    SAME_ORIGIN = "same-origin",
}

class ClsApolloClient {
    private _httpLink: HttpLink;
    private _credentials: EApolloCLientCredentials;
    private _fragmentMatcher: FragmentMatcher | undefined;
    private _graphqlServerUrl: string;
    private _cacheInMemoryRules: Record<string, any>;
    private _apolloClient: ApolloClient<NormalizedCacheObject>;

    constructor(
        graphqlServerUrl: string,
        credentials: EApolloCLientCredentials = EApolloCLientCredentials.DEFAULT_OMIT,
        fragmentMatcher?: FragmentMatcher,
        cacheInMemoryRules: Record<string, any> = {}
    ) {
        this._graphqlServerUrl = graphqlServerUrl;
        this._credentials = credentials;
        this._fragmentMatcher = fragmentMatcher;
        this._cacheInMemoryRules = cacheInMemoryRules;

        this._apolloClient = this.initApolloClient();
    }

    get apolloClient(): ApolloClient<NormalizedCacheObject> {
        return this._apolloClient;
    }

    get httpLink(): HttpLink {
        return this._httpLink;
    }

    get cacheInMemoryRules(): Record<string, any> {
        return this._cacheInMemoryRules;
    }
    set cacheInMemoryRules(cacheInMemoryRules: Record<string, any>) {
        this._cacheInMemoryRules = cacheInMemoryRules;
        this._apolloClient = this.initApolloClient();
    }

    get credentials(): EApolloCLientCredentials {
        return this._credentials;
    }

    set credentials(credentials: EApolloCLientCredentials) {
        this._credentials = credentials;
        this._apolloClient = this.initApolloClient();
    }

    get fragmentMatcher(): FragmentMatcher | undefined {
        return this._fragmentMatcher;
    }

    set fragmentMatcher(fragmentMatcher: FragmentMatcher | undefined) {
        this._fragmentMatcher = fragmentMatcher;
        this._apolloClient = this.initApolloClient();
    }

    private initApolloClient(): ApolloClient<NormalizedCacheObject> {
        const errorLink = onError(({ graphQLErrors, networkError }) => {
            if (graphQLErrors)
                graphQLErrors.forEach(({ message, locations, path }) =>
                    console.log(
                        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
                    )
                );

            if (networkError) console.log(`[Network error]: ${networkError}`);
        });

        this._httpLink = new HttpLink({
            uri: this._graphqlServerUrl,
            credentials: this._credentials,
        });
        const links = [errorLink, this._httpLink];
        const apolloClient: ApolloClient<NormalizedCacheObject> = new ApolloClient(
            {
                link: ApolloLink.from(links),
                cache: new InMemoryCache({
                    ...this._cacheInMemoryRules,
                    addTypename: false,
                }),
                fragmentMatcher: this._fragmentMatcher,
            }
        );
        return apolloClient;
    }
}

export { EApolloCLientCredentials, ClsApolloClient };

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Remember to always call "done()" in Typescript + Mocha/Chai when dealing with async tests and hooks. Additionally, when returning a Promise, make sure it resolves correctly

It seems like I'm facing an old issue that I just can't seem to resolve, despite trying everything in my power. The error message reads: Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Pro ...

Mapping an array of keys to an array of properties using Typescript

Is there a way to achieve the following: type A = { a: string; b: number; c: boolean }; type B = ["b", "a"]; type C = MapProps<A, B> ?? // [number, string] The solution I have currently is: type C = {[key in B[number]]: A[key]} ...

Troubleshooting Next.js and Tailwind CSS Breakpoints: What's causing the

Having trouble with my custom breakpoints. For instance, I attempted the following: <div className="flex flex-row gap-5 mb-5 md:ml-15 sm:ml-15"> ... </div> The margin is not being applied on small and medium screens. Here are the th ...

Leverage TypeScript with AngularJS to validate interpolation and binding within HTML documents

While TypeScript can detect compile errors for *.ts files, the question arises if these benefits can be extended to AngularJS views/templates. Consider a scenario where the code snippet below is present: <div ng-controller="HomeController as home"> ...

Troubleshooting Nested Handlebars Problem

After creating a customized handlebar that checks for equality in this manner: Handlebars.registerHelper('ifEquals', (arg1, arg2, options) => { if (arg1 == arg2) { return options?.fn(this); } return options?.inverse(t ...

React-scripts is not recognizing tests that have the .tsx file extension

Currently in the process of converting my project to TypeScript, everything is almost working perfectly. The code builds without issues and renders correctly. The only hiccup I'm facing is with my tests. I've observed that when I change a test f ...

What causes a folder to disappear after rerunning in nest.js?

When working on my project using nest.js in MacOS Sonoma, I encountered a problem where the image folder src/products/images gets deleted after every project rerun (npm start). The images are saved like this: for (const image of images) { const fileName ...

Assign a value to a date field in Aurelia

<input class="" type="date" id="Broken" value.bind="dateval"> The current value of dateval is 2021-04-08T10:05:19.988Z. Is there a way to set a default date for the date input field above? ...

What is a more organized approach to creating different versions of a data type in Typescript?

In order to have different variations of content types with subtypes (such as text, photo, etc.), all sharing common properties like id, senderId, messageType, and contentData, I am considering the following approach: The messageType will remain fixed f ...

Utilize Typescript to ensure uniformity in object structure across two choices

Looking to create a tab component that can display tabs either with icons or plain text. Instead of passing in the variant, I am considering using Typescript to verify if any of the icons have an attribute called iconName. If one icon has it, then all othe ...

What is the best way to fetch data before a component is rendered on the screen?

I am facing an issue with fetching data from a local server in Node. When I try to render the component, the array 'users' from the state appears to be empty, resulting in no users being displayed on the screen as intended. What's strange is ...

Move forward state using navigateByUrl in Angular

I am looking to send data when changing the view using navigateByUrl like this: this.router.navigateByUrl(url, {state: {hello: "world"}}); Once in the next view, my goal is to simply log the hello attribute like so: constructor(public router: Router) { ...

Extending injections in Angular 5 by inheriting from a base class

I have created a class with the following structure: @Injectable FooService { constructor(protected _bar:BarService){ } } Then, I extended this class as shown below: @Injectable ExtFooService extends FooService { constructor(_bar:BarServi ...

Adding Typescript to a Nativescript-Vue project: A step-by-step guide

Struggling over the past couple of days to configure Typescript in a basic template-generated Nativescript-Vue project has been quite the challenge. Here's my journey: Initiated the project using this command: ERROR in Entry module not found: Erro ...

Steps to activate zone-conscious bluebird assurances in Angular 8

In order to make all the promises in my Angular 8 project cancelable, I embarked on a quest to find the perfect library for the job. It was during this search that I stumbled upon bluebird.js, a promising candidate ;-) Following these guidelines on integr ...

Using Typescript generics within a callback function

I am currently working on developing a versatile service that can fetch data from a remote source and create objects based on that data. @Injectable() export class tService<T> { private _data: BehaviorSubject<T[]> = new BehaviorSubject([]) ...

"Error: The specified object does not have the capability to support the property or method 'includes'." -[object Error]

Recently, I developed a method that utilizes both indexOf() and includes(). However, I encountered an error message stating "Object doesn't support property or method 'includes'". I have attempted to run the method on both Internet Explorer ...

Can a TypeScript generator function be accurately typed with additional functionality?

Generator functions come with prototype properties that allow for the addition of behavior. The generated generators inherit this behavior. Unfortunately, TypeScript does not seem to recognize this feature, leaving me unsure of how to make it aware. For i ...

Encountering an error with TypeScript generic parameters: Expecting '?' but receiving an error message

As a newcomer to TypeScript, I am currently exploring the use of generic type parameters. I have encountered an error message: '?' expected while working on a function and a type that both require type arguments. The issue seems to be in the Inpu ...

Angular Error: The first argument has a property that contains NaN

Struggling with a calculation formula to find the percentage using Angular and Typescript with Angularfire for database storage. Encountered an error stating First argument contains NaN in property 'percent.percentKey.percentMale. The properties are d ...