Skip to content
5 changes: 5 additions & 0 deletions .changeset/solid-queryoptions-initialdata-overload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/solid-query': patch
---

fix(solid-query/{queryOptions,useQuery}): correct the overload selected for 'initialData'
9 changes: 9 additions & 0 deletions packages/solid-query/src/__tests__/queryOptions.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ describe('queryOptions', () => {

expectTypeOf(tagged[dataTagSymbol]).toEqualTypeOf<number>()
})
it('should tag the queryKey with the result type of the QueryFn when initialData may be undefined', () => {
const { queryKey: tagged } = queryOptions({
queryKey: queryKey(),
queryFn: () => Promise.resolve(5),
initialData: Math.random() > 0.5 ? 5 : undefined,
})

expectTypeOf(tagged[dataTagSymbol]).toEqualTypeOf<number>()
})
it('should tag the queryKey even if no promise is returned', () => {
const { queryKey: tagged } = queryOptions({
queryKey: queryKey(),
Expand Down
10 changes: 8 additions & 2 deletions packages/solid-query/src/queryOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {
DefaultError,
InitialDataFunction,
NonUndefinedGuard,
QueryKey,
QueryKeyWithDataTag,
} from '@tanstack/query-core'
Expand All @@ -24,7 +26,10 @@ export type UndefinedInitialDataOptions<
TQueryKey extends QueryKey = QueryKey,
> = Accessor<
QueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
initialData?: undefined
initialData?:
| undefined
| InitialDataFunction<NonUndefinedGuard<TQueryFnData>>
| NonUndefinedGuard<TQueryFnData>
}
>

Expand All @@ -44,7 +49,8 @@ export type DefinedInitialDataOptions<
TQueryKey extends QueryKey = QueryKey,
> = Accessor<
QueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
initialData: TQueryFnData | (() => TQueryFnData)
initialData:
NonUndefinedGuard<TQueryFnData> | (() => NonUndefinedGuard<TQueryFnData>)
}
>

Expand Down
104 changes: 52 additions & 52 deletions packages/solid-query/src/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,58 @@ import type {
UndefinedInitialDataOptions,
} from './queryOptions'

/**
* Subscribes to a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* The query runs when the options call for it β€” `enabled: false` skips the initial fetch.
*
* This overload is selected when `initialData` is set, so the resulting `data` is never `undefined` (unless
* a `select` changes `TData` to include `undefined`).
*
* @see {@link queryOptions} to share these options between `useQuery` and imperative APIs like `queryClient.query`.
* @param options - An accessor returning the {@link DefinedInitialDataOptions} to use β€” everything you can
* pass to `useQuery`, with `initialData` set.
* @param queryClient - An accessor for a custom `QueryClient`. Otherwise, the one from the nearest context
* will be used.
* @returns The current query result, as a Solid store, typed so that `status` is `success` β€” or `error` if a
* fetch attempt fails while keeping the existing data (`status` never resolves to `pending` in this overload's
* type, since `initialData` guarantees data upfront). `isSuccess`/`isError` are derived booleans for
* convenience.
*
* @example
* ```tsx
* import { For } from 'solid-js'
* import { useQuery } from '@tanstack/solid-query'
*
* function Posts() {
* // `postsQuery.data` is never `undefined`, thanks to `initialData` β€” even if a refetch fails, so the
* // list stays visible alongside the error.
* const postsQuery = useQuery(() => ({
* queryKey: ['posts'],
* queryFn: fetchPosts,
* initialData: [],
* }))
*
* return (
* <div>
* {postsQuery.isError ? <span>Error: {postsQuery.error.message}</span> : null}
* <ul>
* <For each={postsQuery.data}>{(post) => <li>{post.title}</li>}</For>
* </ul>
* </div>
* )
* }
* ```
*/
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: () => QueryClient,
): DefinedUseQueryResult<TData, TError>

/**
* Subscribes to a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* The query runs when the options call for it β€” `enabled: false` skips the initial fetch.
Expand Down Expand Up @@ -191,58 +243,6 @@ export function useQuery<
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: () => QueryClient,
): UseQueryResult<TData, TError>

/**
* Subscribes to a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* The query runs when the options call for it β€” `enabled: false` skips the initial fetch.
*
* This overload is selected when `initialData` is set, so the resulting `data` is never `undefined` (unless
* a `select` changes `TData` to include `undefined`).
*
* @see {@link queryOptions} to share these options between `useQuery` and imperative APIs like `queryClient.query`.
* @param options - An accessor returning the {@link DefinedInitialDataOptions} to use β€” everything you can
* pass to `useQuery`, with `initialData` set.
* @param queryClient - An accessor for a custom `QueryClient`. Otherwise, the one from the nearest context
* will be used.
* @returns The current query result, as a Solid store, typed so that `status` is `success` β€” or `error` if a
* fetch attempt fails while keeping the existing data (`status` never resolves to `pending` in this overload's
* type, since `initialData` guarantees data upfront). `isSuccess`/`isError` are derived booleans for
* convenience.
*
* @example
* ```tsx
* import { For } from 'solid-js'
* import { useQuery } from '@tanstack/solid-query'
*
* function Posts() {
* // `postsQuery.data` is never `undefined`, thanks to `initialData` β€” even if a refetch fails, so the
* // list stays visible alongside the error.
* const postsQuery = useQuery(() => ({
* queryKey: ['posts'],
* queryFn: fetchPosts,
* initialData: [],
* }))
*
* return (
* <div>
* {postsQuery.isError ? <span>Error: {postsQuery.error.message}</span> : null}
* <ul>
* <For each={postsQuery.data}>{(post) => <li>{post.title}</li>}</For>
* </ul>
* </div>
* )
* }
* ```
*/
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: () => QueryClient,
): DefinedUseQueryResult<TData, TError>
export function useQuery<
TQueryFnData,
TError = DefaultError,
Expand Down
Loading