TanStack / query

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.
https://tanstack.com/query
MIT License
42.72k stars 2.93k forks source link

Cannot pass an optional infiniteData parameter to infiniteQueryOptions function #8132

Closed jimmycallin closed 1 month ago

jimmycallin commented 1 month ago

Describe the bug

It doesn't seem possible to pass an optional initialData parameter to infiniteQueryOptions, while it does work for useInfiniteQuery.

Example query not working:

See playground example here.

Your minimal, reproducible example

https://www.typescriptlang.org/play/?ssl=18&ssc=1&pln=19&pc=1#code/JYWwDg9gTgLgBAbzgVwM4FMCSA7AZsbYGdARWXSgE84BfOXKCEOAIgAEYBDbVLgYwDWAeijpOfGAFoAjuSosA3ACgluZNgnAI2FBgDqUTmDAUAFEgJFgnADYARTl1oAuRHEsxr9x5wD8rpHQAD05wG3RXGChyWloASkQlODg+bV44WQpKAHkwTzS4AF53PA9SORy8rR5zJOSMioBpdEpXAG0WYNCwcJYAXQAaOuTMqgAxbFdOVEoNOFMEwoA+EqtbBy4h+tXPdZ9XDy8NzmH633nFlfM4ME4Ac3RUdsO9rkGb+-QABU5DECe4G0+vFTslXOoACbofDYdAQrb1B4wABywRgPwePz+rgWRRWAEYEckXjYMd9fqFXIS6jQ4soaCo1Bp8jo0OgDEYTFAyFlriTji43PyfP43F0whE4FEYnRaYlkqIYMgoKyMDgYURyryEKdRpRmq1AZ0QhL+kSGlkJlMZnNccsdkcfObhVwDoRdt4uKC4Oc7VckLcHgC2i7OO9A+S-sHgbTveDsFCYXDzUjUUF0Z8sZSLni4NTtiSyVmQFSEbT6UA

Steps to reproduce

function useWrapper({ initialData }: { initialData?: { example: true } }) {
  const queryOptions = infiniteQueryOptions({
    queryKey: ["example"],
    queryFn: async () => initialData,
   // initialData below errors
    initialData: initialData
      ? () => ({ pages: [initialData], pageParams: [] })
      : undefined,
    getNextPageParam: () => 1,
    initialPageParam: 1,
  });
}

Typescript error:

No overload matches this call.
  Overload 1 of 2, '(options: DefinedInitialDataInfiniteOptions<{ example: true; } | undefined, Error, InfiniteData<{ example: true; } | undefined, unknown>, string[], number>): UseInfiniteQueryOptions<...> & ... 1 more ... & { ...; }', gave the following error.
    Type '(() => { pages: { example: true; }[]; pageParams: never[]; }) | undefined' is not assignable to type '(InfiniteData<{ example: true; } | undefined, number> | InitialDataFunction<InfiniteData<{ example: true; } | undefined, number>> | undefined) & (InfiniteData<...> | (() => InfiniteData<...>))'.
      Type 'undefined' is not assignable to type '(InfiniteData<{ example: true; } | undefined, number> | InitialDataFunction<InfiniteData<{ example: true; } | undefined, number>> | undefined) & (InfiniteData<...> | (() => InfiniteData<...>))'.
  Overload 2 of 2, '(options: UndefinedInitialDataInfiniteOptions<{ example: true; } | undefined, Error, InfiniteData<{ example: true; } | undefined, unknown>, string[], number>): UseInfiniteQueryOptions<...> & ... 1 more ... & { ...; }', gave the following error.
    Type '(() => { pages: { example: true; }[]; pageParams: never[]; }) | undefined' is not assignable to type 'undefined'.
      Type '() => { pages: { example: true; }[]; pageParams: never[]; }' is not assignable to type 'undefined'.(2769)

Expected behavior

Equivalent example using useInfiniteQuery that works fine:

function useWrapperQuery({ initialData }: { initialData?: { example: true } }) {
  return useInfiniteQuery({
    queryKey: ["example"],
    queryFn: async () => initialData,
    initialData: initialData
      ? () => ({ pages: [initialData], pageParams: [] })
      : undefined,
    getNextPageParam: () => 1,
    initialPageParam: 1,
  });
}

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

N/A

Tanstack Query adapter

react-query

TanStack Query version

v5.59.0

TypeScript version

v5.6.2

Additional context

No response

ddoemonn commented 1 month ago

@jimmycallin You have to use initialPageParam like this:

function useWrapperQuery() {
  const options = infiniteQueryOptions({
    initialPageParam: 1,
    queryKey: ["users"],
    queryFn: async ({ pageParam = 1 }) => {
      const response = await fetch(`https://randomuser.me/api/?page=${pageParam}&results=10`);
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    },
    getNextPageParam: (lastPage, allPages) => {
      const nextPage = allPages.length + 1;
      return nextPage <= 100 ? nextPage : undefined;
    },
  });

  return useInfiniteQuery(options);
}
jimmycallin commented 1 month ago

@jimmycallin You have to use initialPageParam like this:

function useWrapperQuery() { const options = infiniteQueryOptions({ initialPageParam: 1, queryKey: ["users"], queryFn: async ({ pageParam = 1 }) => { const response = await fetch(https://randomuser.me/api/?page=${pageParam}&results=10); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }, getNextPageParam: (lastPage, allPages) => { const nextPage = allPages.length + 1; return nextPage <= 100 ? nextPage : undefined; }, });

return useInfiniteQuery(options); }

I don't quite understand how this helps - what does this have to do with the issue I have with initialData?

sungpaks commented 1 month ago

It seems that this error occurs because the type of infiniteQueryOptions is actually devided in two types, DefinedInitialDataInfiniteOptions and UndefinedInitialDataInfiniteOptions.
So, TypeScript now tries to guarantee the type by using one or the other, but neither of them allow initialData | undefined, so we get this error.
🤔 I still wonder why infiniteQueryOptions type had to be like this.

Anyway, to silence this error temporarily, you can do like :

const queryOptions = initialData ? infiniteQueryOptions({
    queryKey: ["example"],
    queryFn: async () => initialData,
    initialData: () => ({ pages: [initialData], pageParams: [] }),
    getNextPageParam: () => 1,
    initialPageParam: 1,
  }) : infiniteQueryOptions({
    queryKey: ["example"],
    queryFn: async () => initialData,
    initialData: undefined,
    getNextPageParam: () => 1,
    initialPageParam: 1,
  })

It does fatten up the code unnecessarily, but.. this is one of the way.

TkDodo commented 1 month ago

but neither of them allow initialData | undefined

UndefinedInitialDataInfiniteOptions definitely should allow it. Compare with the types for queryOptions

sungpaks commented 1 month ago

~~I fixed definedInitialQueryOptions to allow and opened a PR. I'll modify it again to allow unDefinedInitialQueryOptions, based on what you've told, is that okay?~~

The above is reflected in the PR