Apollo-client: Type 'ApolloLink' is not assignable to type 'ApolloLink'

Created on 3 Mar 2020  路  37Comments  路  Source: apollographql/apollo-client

Intended outcome:
This TypeScript error occurs with a link chain using additive composition and @apollo/client.
The goal is to create an Apollo Link composed of an Error Link, a Context Link and an HttpLink or HttpBatchLink.

import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from '@apollo/client';
import { BatchHttpLink } from '@apollo/link-batch-http';
import { onError } from '@apollo/link-error';
import AuthLink from './authLink'

let httpLink: HttpLink | BatchHttpLink;
if (enableBatching === true) {
  httpLink = new BatchHttpLink({
    ...authenticatedHttpSettings,
    batchMax: 30,
  });
} else {
  httpLink = new HttpLink(authenticatedHttpSettings);
}

const errorLink = onError(({ graphQLErrors, operation }) => {
  if (graphQLErrors) {...}
});

const link = from([errorLink, AuthLink(), httpLink]);

  const testApi = new ApolloClient({
    connectToDevTools: true,
    link,
    cache: new InMemoryCache(),
  })

Actual outcome:
TypeScript throws an error:
Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.

How to reproduce the issue:
I assume the issue is related to different type definitions for Apollo Link. This should be reproducible with the given code in a Typescript / React environment.

How to solve this issue
Currently @apollo/link-http-batch v.2.0.0-beta.3 is using @apollo/client v.3.0.0-beta.23. Downgrading my @apollo/client to 3.0.0-beta.23 worked, but to solve this issue the @apollo/client version should be upgraded in all link packages.

Versions

"react": "^16.12.0",
"react-dom": "^16.12.0",
 "typescript": "^3.7.3",
"@apollo/client": "^3.0.0-beta.38",
"@apollo/link-batch-http": "^2.0.0-beta.3",
"@apollo/link-context": "^2.0.0-beta.3",
"@apollo/link-error": "^2.0.0-beta.3",
"apollo": "^2.24.0",

Full Stack Trace
Type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink' is not assignable to type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink'. Types of property 'split' are incompatible. Type '(test: (op: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/...' is not assignable to type '(test: (op: import("/path/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path...'. Types of parameters 'left' and 'left' are incompatible. Type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/client/link/core/types").RequestHandler' is not assignable to type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Reque...'. Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'. Type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink' is not assignable to type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink'. Types of property 'split' are incompatible. Type '(test: (op: import("/path/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path...' is not assignable to type '(test: (op: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/...'. Types of parameters 'left' and 'left' are incompatible. Type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Reque...' is not assignable to type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/client/link/core/types").RequestHandler'. Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.

Most helpful comment

@dep-dt You should import like this and that is what @afroald mentioned above.

import { onError } from "@apollo/client/link/error";
import { setContext } from "@apollo/client/link/context";

Remove those packages @apollo/link-context and @apollo/link-error as those are already included in the @apollo/client

All 37 comments

same issue, code is identical to docs: https://www.apollographql.com/docs/react/v3.0-beta/networking/authentication/ > header

and am importing useContext from @apollo/link-context

Also seeing this updating from beta.36 to beta.39. Also using useContext from @apollo/[email protected]

"apollo-link": "^1.2.3",
"apollo-link-context": "^1.0.19",
"apollo-link-http": "^1.5.16",

I am running into the same error while using setContext. I am using the above mentioned package version. Any thoughts how I can resolve this error?

also saw this error upgrading @apollo/client from beta.38 -> beta.39 when using setContext from @apollo/[email protected]

Screenshot 2020-03-24 at 16 46 08
The same issue here...

I'm not using the errorLink at all any ideas?

The warning is gone after upgrading to @apollo/[email protected].

I still have this error, so I used this approach:

link: from([(errorLink as unknown) as ApolloLink, httpLink]),

This issue seems to be resolved in beta.43, but present from beta.44 onward.

@austin43 Thanks for the info, we'll hold on upgrading until this regression is fixed.

@vicary to get around the blocker temporarily, @wamujlb's solution is certainly viable. It is just a types issue so shouldn't affect anything during runtime.

For me upgrading client and link-error fixed the problem:
"@apollo/client": "^3.0.0-beta.49", "@apollo/link-error": "^2.0.0-beta.3"

@GiselaMD Not sure if I've been missing something, tried beta.49 and beta.50 and they both gave me type errors on all @apollo/link-error, @apollo/link-context and @apollo/link-retry.

  "devDependencies": {
    "@apollo/client": "3.0.0-beta.49",
    "@apollo/link-context": "^2.0.0-beta.3",
    "@apollo/link-error": "^2.0.0-beta.3",
    "@apollo/link-retry": "^2.0.0-beta.3",

@vicary try fixing @apollo/client version inside @apollo/link-error and other apollo dependencies, they must be the same version inside those packages. In my case I use yarn, so I set yarn resolution in my package like this, for example:

"resolutions": {
    "@apollo/client": "3.0.0-beta.43",
  }

@GiselaMD So are we forcing the deps of @apollo/client inside @apollo/link-* to match the top-level, such that a dependencies of beta.49 should set resolutions to beta.49, while beta.43 should forces a beta.43?

Yes @vicary

I'm still seeing this TypeScript error with @apollo/client 3.0.0-rc.2.

"@apollo/client": "3.0.0-rc.2",
"@apollo/link-context": "^2.0.0-beta.3",
"@apollo/link-error": "^2.0.0-beta.3",
"@apollo/link-retry": "^2.0.0-beta.3",

Any known workarounds now with the RC?

same with

"@apollo/client": "^3.0.0-rc.4",
"@apollo/link-context": "^2.0.0-beta.3",

Same with
"@apollo/client": "^3.0.0-rc.4", "@apollo/link-context": "^2.0.0-beta.3",

They opened a can of worm on maintaining cross dependency of a common core package, when it really should be as simple as a peer dependency. What a shame.

is there any fix?

i tried "resolution" 49 beta trip mentioned above, didn't work for me

import Cookie from 'js-cookie';
import config from 'config';
import {
    ApolloClient,
    createHttpLink,
    InMemoryCache,
    ApolloLink,
} from '@apollo/client';
import { WebSocketLink } from '@apollo/link-ws';
import { setContext } from 'apollo-link-context';
import { onError } from '@apollo/link-error';

const wsLink = new WebSocketLink({
    uri: config.server.graphQLWs,
    options: {
        reconnect: true,
    },
});

const httpLink = createHttpLink({
    uri: config.server.graphQLApi,
});

const cache = new InMemoryCache({
    addTypename: false,
});

const errorLink: any = 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}`);
    }
});

const authLink: any = setContext((_, { headers, ...context }) => {
    // get the authentication token from local storage if it exists
    const token = Cookie.get('userToken');
    // return the headers to the context so httpLink can read them
    return {
        headers: {
            ...headers,
            authorization: token ? `Bearer ${token}` : '',
        },
        ...context,
    };
});

const hasSubscriptionOperation = ({ query: { definitions } }) =>
    definitions.some(
        ({ kind, operation }) =>
            kind === 'OperationDefinition' && operation === 'subscription'
    );

export const client = new ApolloClient({
    version: '1',
    resolvers: {},
    cache: cache,
    link: ApolloLink.from([
        errorLink,
        authLink,
        ApolloLink.split(hasSubscriptionOperation, wsLink, httpLink),
    ]),
    queryDeduplication: false,
});

that's my fix

I still have this issue with @apollo/client:3.0.2

I still have this issue with @apollo/client:3.1.0

My issue was with the ApolloProvider. After upgrading to. @apollo/client 3.1.3 i was still importing ApolloProvider from @apollo/react-hooks. Simply imported it from @apollo/client instead and everything works fine.

I am using "@apollo/client": "^3.1.3" and still have same issue. Any update ?

The solution for me was to use the links included in @apollo/client instead of the @apollo/link-* packages. See the part about links in the migration docs for more information:

https://www.apollographql.com/docs/react/migrating/apollo-client-3-migration/#apollo-link-

@afroald It seems working now 馃挴

@afroald's solution didn't work for me. Same error.

  "dependencies": {
    "@apollo/client": "3.1.3",
    "@apollo/link-context": "2.0.0-beta.3",
    "@apollo/link-error": "2.0.0-beta.3",
    ...
}

and

import { HttpLink, ApolloClient, ApolloLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/link-context';
import { onError } from '@apollo/link-error';

interface ProviderProps {
    apiUrl?: string;
    ibpCookie?: string;
}

function TitanGqlClient({ apiUrl, ibpCookie }: ProviderProps) {
    const httpLink = new HttpLink({
        uri: apiUrl || 'http://localhost:4000/graphql',
    });

    const authLink: any = setContext(
        (_, { headers }) => ibpCookie && {
            headers: {
                ...headers,
                'X-Cookie': ibpCookie,
            },
        },
    );

    const errorLink: any = onError(({ graphQLErrors, networkError }) => {
        if (graphQLErrors) {
            // eslint-disable-next-line no-console
            graphQLErrors.map(({ message, locations, path }) => console.log(
                    `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
            ),
            );
        }

        if (networkError) {
            // eslint-disable-next-line no-console
            console.log(`[Network error]: ${networkError}`);
        }
    });

    const link = ApolloLink.from([errorLink, authLink.concat(httpLink)]);

    const client = new ApolloClient({
        link,
        cache: new InMemoryCache(),
    });

    return client;
}

export { TitanGqlClient };

Shows

src/components/App.tsx(13,25): error TS2322: Type 'ApolloClient<NormalizedCacheObject>' is not assignable to type 'ApolloClient<any>'.
  The types of 'link.split' are incompatible between these types.
    Type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/c...' is not assignable to type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_mo...'.
      Types of parameters 'left' and 'left' are incompatible.
        Type 'import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").RequestHandler' is not assignable to type 'import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").RequestHand...'.
          Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.
            Type 'import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink' is not assignable to type 'import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink'.
              Types of property 'split' are incompatible.
                Type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_mo...' is not assignable to type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/c...'.
                  Types of parameters 'left' and 'left' are incompatible.
                    Type 'import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").RequestHand...' is not assignable to type 'import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").RequestHandler'.
                      Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.

@dep-dt You should import like this and that is what @afroald mentioned above.

import { onError } from "@apollo/client/link/error";
import { setContext } from "@apollo/client/link/context";

Remove those packages @apollo/link-context and @apollo/link-error as those are already included in the @apollo/client

@khanakia This worked thanks. Also thanks @afroald :)

I still have this issue with: "@apollo/client": "^3.1.4"

....
const wsLink = new WebSocketLink({
  uri: `ws://localhost:3000/graphql`,
  options: {
    reconnect: true,
  },
});

const httpLink = createHttpLink({
  uri: 'http://localhost:3000/graphql',
});

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = localStorage.getItem('accessToken');
  console.log(token);
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});


const client = new ApolloClient({
  link: from([authLink.concat(httpLink), wsLink]), //~> first one will work but second one (wsLink)
  cache: new InMemoryCache(),
});

@benyou1969

I think at least part of your problem is the construction of createHttpLink ... This has changed recently. Try something like this:

import { HttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = new HttpLink({
    uri: 'http://localhost:3000/graphql',
});

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = localStorage.getItem('accessToken');
  console.log(token);
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});

Here's a full working example of mine, if it helps anyone who ends up here.

import { HttpLink, ApolloClient, ApolloLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';

const httpLink = new HttpLink({
    uri: 'http://localhost:4000/graphql',
});

// we are appending a custom cookie
const authLink = setContext(
    (_, { headers }) => fooCookie && {
        headers: {
            ...headers,
            'X-Cookie': fooCookie,
        },
    },
);

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

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

// Prevent ApolloClient from retrying infinitely on failures
// Doc: https://www.apollographql.com/docs/link/links/retry/
const retryLink = new RetryLink({
    delay: {
        initial: 300,
        max: 20000,
        jitter: true,
    },
    attempts: {
        max: 3,
        retryIf: error => !!error,
    },
});

const link = ApolloLink.from([errorLink, retryLink, authLink, httpLink]);

const client = new ApolloClient({
    link,
    cache: new InMemoryCache(),
});

@dep-dt First Thank you for replying.
I tried it but still get the same result, I think the problem is when I try to add wsLink and httpLink to link:
I can't use both of them, always going to run the first one only.
image

or

image

According to this article I see

"Apollo Client should use your WebSocketLink for subscriptions, but it shouldn't use it for queries or mutations. For those operations, Apollo Client should use HTTP as usual. To support this, the @apollo/client library provides a split function that lets you use one of two different Links, according to the result of a boolean check."

So maybe you need to do something like this?

import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { WebSocketLink } from '@apollo/client/link/ws';

const wsLink = new WebSocketLink({
  uri: `ws://localhost:3000/graphql`,
  options: {
    reconnect: true,
  },
});

const httpLink = new HttpLink({
  uri: 'http://localhost:3000/graphql',
});

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = localStorage.getItem('accessToken');
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});

// The split function takes three parameters:
//
// * A function that's called for each operation to execute
// * The Link to use for an operation if the function returns a "truthy" value
// * The Link to use for an operation if the function returns a "falsy" value
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: ApolloLink.from([splitLink, authLink]),
  cache: new InMemoryCache(),
});

Apologies in advance, I've not worked with WebSocketLink much.

According to this article I see

"Apollo Client should use your WebSocketLink for subscriptions, but it shouldn't use it for queries or mutations. For those operations, Apollo Client should use HTTP as usual. To support this, the @apollo/client library provides a split function that lets you use one of two different Links, according to the result of a boolean check."

So maybe you need to do something like this?

import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { WebSocketLink } from '@apollo/client/link/ws';

const wsLink = new WebSocketLink({
  uri: `ws://localhost:3000/graphql`,
  options: {
    reconnect: true,
  },
});

const httpLink = new HttpLink({
  uri: 'http://localhost:3000/graphql',
});

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = localStorage.getItem('accessToken');
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});

// The split function takes three parameters:
//
// * A function that's called for each operation to execute
// * The Link to use for an operation if the function returns a "truthy" value
// * The Link to use for an operation if the function returns a "falsy" value
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: ApolloLink.from([splitLink, authLink]),
  cache: new InMemoryCache(),
});

Apologies in advance, I've not worked with WebSocketLink much.

@dep-dt Dude thank you very much for taking the time to help me find the solution,
and I worked when I switched

 const client = new ApolloClient({
   link: ApolloLink.from([splitLink, authLink]),
   cache: new InMemoryCache(),
 });

to

const client = new ApolloClient({
  link: ApolloLink.from([authLink, splitLink]),
  cache: new InMemoryCache(),
});

thanks again!

@benyou1969 great news!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gregorskii picture gregorskii  路  3Comments

eweilow picture eweilow  路  3Comments

joergbaier picture joergbaier  路  3Comments

treecy picture treecy  路  3Comments

stubailo picture stubailo  路  3Comments