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.44k stars 2.9k forks source link

Type error when using experimental_createPersister with a queryFn that has an argument #7842

Open EliasWatson opened 3 months ago

EliasWatson commented 3 months ago

Describe the bug

When using experimental_createPersister from @tanstack/query-persist-client-core, TypeScript throws a type error in queryOptions() when queryFn has an argument, but doesn't throw a type error if queryFn has no arguments. The type error goes away if we define the generic argument on queryOptions().

Your minimal, reproducible example

https://codesandbox.io/p/github/EliasWatson/tanstack-query-persist-bug-example/main?import=true

Steps to reproduce

  1. Write a function to create a QueryPersister using experimental_createPersister.
    function createPersister<TData>(): QueryPersister<TData, any> {
    return experimental_createPersister({ storage: undefined });
    }
  2. Create query options using the persister function with a queryFn that has no arguments. Notice that there are no TypeScript errors.
    queryOptions({
    queryKey: ['good-query'],
    queryFn: () => 'test',
    persister: createPersister<string>(),
    });
  3. Add an argument to the queryFn. Notice that TypeScript now throws an error.
    queryOptions({
    queryKey: ['bad-query'],
    // Ignoring the unused variable error:
    // @ts-expect-error
    queryFn: (args) => 'test',
    persister: createPersister<string>(), // Type error here!
    });
    
    src/App.tsx:23:5 - error TS2769: No overload matches this call.
    Overload 1 of 2, '(options: DefinedInitialDataOptions<unknown, Error, unknown, string[]>): UseQueryOptions<unknown, Error, unknown, string[]> & { initialData: unknown; } & { ...; }', gave the following error.
    Types of parameters 'queryFn' and 'queryFn' are incompatible.
      Type 'unknown' is not assignable to type 'string | Promise<string>'.
    Overload 2 of 2, '(options: UndefinedInitialDataOptions<unknown, Error, unknown, string[]>): UseQueryOptions<unknown, Error, unknown, string[]> & { initialData?: InitialDataFunction<...> | undefined; } & { ...; }', gave the following error.
    Type '(queryFn: QueryFunction<string, any, never>, context: { queryKey: any; signal: AbortSignal; meta: Record<string, unknown> | undefined; pageParam?: unknown; direction?: unknown; }, query: Query<...>) => string | Promise<...>' is not assignable to type '(queryFn: QueryFunction<unknown, string[], never>, context: { queryKey: string[]; signal: AbortSignal; meta: Record<string, unknown> | undefined; pageParam?: unknown; direction?: unknown; }, query: Query<...>) => unknown'.

23 persister: createPersister(),


  node_modules/@tanstack/query-core/build/modern/hydration-DtrabBHC.d.ts:577:5
    577     persister?: QueryPersister<NoInfer<TQueryFnData>, NoInfer<TQueryKey>, NoInfer<TPageParam>>;
The expected type comes from property 'persister' which is declared here on type 'DefinedInitialDataOptions<unknown, Error, unknown, string[]>'

node_modules/@tanstack/query-core/build/modern/hydration-DtrabBHC.d.ts:577:5 577 persister?: QueryPersister<NoInfer, NoInfer, NoInfer>;


    The expected type comes from property 'persister' which is declared here on type 'UndefinedInitialDataOptions<unknown, Error, unknown, string[]>'
```
4. Specify the generic argument on `queryOptions`. Notice that the TypeScript error goes away.
```ts
queryOptions<string>({
  queryKey: ['fixed-bad-query'],
  // Ignoring the unused variable error:
  // @ts-expect-error
  queryFn: (args) => 'test',
  persister: createPersister<string>(),
});
```

### Expected behavior

I expect TypeScript to not throw a type error when `queryFn` has an argument, just like it does when `queryFn` doesn't have arguments. I also don't want to have to specify the generic type on `queryOptions` since TypeScript has no problem inferring it when `queryFn` has no arguments.

### How often does this bug happen?

Every time

### Screenshots or Videos

_No response_

### Platform

- OS: MacOS
- Browser: Google Chrome
- Version: 127.0.6533.89 (Official Build) (arm64)

### Tanstack Query adapter

react-query

### TanStack Query version

v5.51.18

### TypeScript version

v5.2.2

### Additional context

_No response_
TkDodo commented 3 months ago

it works if you omit the return type declaration QueryPersister<TData, any>:

I think it's related to "Intra expression inference" maybe ? TypeScript does inference differently if function parameters are used. Could be related to:

You can see that if you hover queryOptions where it errors, it's actually inferred as queryOptions<unknown, Error, unknown, string[]>. So data is of type unknown instead of string, which I think is because it can't be inferred from the queryFn for "reasons" 🤷