Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-cats-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/vue-query': patch
---

Allow optional initial data and initializer functions returning undefined in `infiniteQueryOptions`, while preserving the overload for guaranteed initial data.
38 changes: 19 additions & 19 deletions docs/framework/vue/reference/functions/infiniteQueryOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ title: infiniteQueryOptions
## Call Signature

```ts
function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options: UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>): UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData, unknown>, TError>;
function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options: DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>): DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData, unknown>, TError>;
```

Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:99](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L99)
Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:110](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L110)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

You can generally pass everything to `infiniteQueryOptions` that you can also pass to `useInfiniteQuery`.
These options can be shared across hooks and imperative APIs such as `queryClient.infiniteQuery`.
`options.queryKey` is required and is the query key to generate options for.

This overload is selected when `initialData` is statically guaranteed to produce defined data, so the resulting `data` is never `undefined`.

### Type Parameters

#### TQueryFnData
Expand All @@ -41,14 +43,14 @@ These options can be shared across hooks and imperative APIs such as `queryClien

#### options

[`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
[`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>

The [UndefinedInitialDataInfiniteOptions](../type-aliases/UndefinedInitialDataInfiniteOptions.md) to use β€” everything you can pass to
`useInfiniteQuery`.
The [DefinedInitialDataInfiniteOptions](../type-aliases/DefinedInitialDataInfiniteOptions.md) to use β€” everything you can pass to
`useInfiniteQuery`, with `initialData` statically guaranteed to produce defined data.

### Returns

[`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & [`QueryKeyWithDataTag`](../type-aliases/QueryKeyWithDataTag.md)\<`TQueryKey`, [`InfiniteData`](../interfaces/InfiniteData.md)\<`TQueryFnData`, `unknown`\>, `TError`\>
[`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & [`QueryKeyWithDataTag`](../type-aliases/QueryKeyWithDataTag.md)\<`TQueryKey`, [`InfiniteData`](../interfaces/InfiniteData.md)\<`TQueryFnData`, `unknown`\>, `TError`\>

The same options object, typed so that `queryKey` carries the inferred data type.

Expand All @@ -67,26 +69,27 @@ const projectsOptions = infiniteQueryOptions({
queryFn: ({ pageParam }) => fetchProjects(pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextId,
initialData: { pages: [], pageParams: [] },
})

const { data, isError, error, fetchNextPage } = useInfiniteQuery(projectsOptions)
// `data` is never `undefined`, thanks to `initialData` β€” even if a refetch fails, so the
// list stays visible alongside the error.
const { data, isError, error } = useInfiniteQuery(projectsOptions)
</script>
```

## Call Signature

```ts
function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options: DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>): DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData, unknown>, TError>;
function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options: UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>): UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData, unknown>, TError>;
```

Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:153](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L153)
Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:159](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L159)

You can generally pass everything to `infiniteQueryOptions` that you can also pass to `useInfiniteQuery`.
These options can be shared across hooks and imperative APIs such as `queryClient.infiniteQuery`.
`options.queryKey` is required and is the query key to generate options for.

This overload is selected when `initialData` is set, so the resulting `data` is never `undefined`.

### Type Parameters

#### TQueryFnData
Expand All @@ -113,14 +116,14 @@ This overload is selected when `initialData` is set, so the resulting `data` is

#### options

[`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>
[`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>

The [DefinedInitialDataInfiniteOptions](../type-aliases/DefinedInitialDataInfiniteOptions.md) to use β€” everything you can pass to
`useInfiniteQuery`, with `initialData` set.
The [UndefinedInitialDataInfiniteOptions](../type-aliases/UndefinedInitialDataInfiniteOptions.md) to use β€” everything you can pass to
`useInfiniteQuery`.

### Returns

[`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & [`QueryKeyWithDataTag`](../type-aliases/QueryKeyWithDataTag.md)\<`TQueryKey`, [`InfiniteData`](../interfaces/InfiniteData.md)\<`TQueryFnData`, `unknown`\>, `TError`\>
[`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & [`QueryKeyWithDataTag`](../type-aliases/QueryKeyWithDataTag.md)\<`TQueryKey`, [`InfiniteData`](../interfaces/InfiniteData.md)\<`TQueryFnData`, `unknown`\>, `TError`\>

The same options object, typed so that `queryKey` carries the inferred data type.

Expand All @@ -139,11 +142,8 @@ const projectsOptions = infiniteQueryOptions({
queryFn: ({ pageParam }) => fetchProjects(pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextId,
initialData: { pages: [], pageParams: [] },
})

// `data` is never `undefined`, thanks to `initialData` β€” even if a refetch fails, so the
// list stays visible alongside the error.
const { data, isError, error } = useInfiniteQuery(projectsOptions)
const { data, isError, error, fetchNextPage } = useInfiniteQuery(projectsOptions)
</script>
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ title: DefinedInitialDataInfiniteOptions
type DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> = UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & object;
```

Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:48](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L48)
Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:54](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L54)

The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set β€” `data` is
never `undefined`.
The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is statically
guaranteed to produce defined data β€” `data` is never `undefined`.

## Type Declaration

### initialData

```ts
initialData:
initialData:
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
| () => NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>;
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ title: UndefinedInitialDataInfiniteOptions
type UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> = UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & object;
```

Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:21](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L21)
Defined in: [packages/vue-query/src/infiniteQueryOptions.ts:22](https://github.com/TanStack/query/blob/main/packages/vue-query/src/infiniteQueryOptions.ts#L22)

The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set β€” `data`
may be `undefined` while the query is `pending`.
The options accepted by the `infiniteQueryOptions` overload selected when `initialData` may be
`undefined` β€” `data` may be `undefined` while the query is `pending`.

## Type Declaration

### initialData?

```ts
optional initialData: undefined;
optional initialData:
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
| InitialDataFunction<NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>>;
```

## Type Parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { describe, expectTypeOf, it } from 'vitest'
import { dataTagSymbol } from '@tanstack/query-core'
import { infiniteQueryOptions } from '../infiniteQueryOptions'
import { useInfiniteQuery } from '../useInfiniteQuery'
import type { InfiniteData } from '@tanstack/query-core'
import type { DefinedInitialDataInfiniteOptions } from '../infiniteQueryOptions'

const queryOptions = {
queryKey: ['initial-data'],
queryFn: ({ pageParam }: { pageParam: number }) =>
Promise.resolve(`${pageParam}`),
initialPageParam: 0,
getNextPageParam: () => 1,
}

describe('infiniteQueryOptions initialData', () => {
it('accepts data that may be undefined without losing page inference', () => {
const createOptions = (initialData?: InfiniteData<string, number>) =>
infiniteQueryOptions({ ...queryOptions, initialData })

const options = createOptions()
const { data } = useInfiniteQuery(options)

expectTypeOf(data.value).toEqualTypeOf<InfiniteData<string> | undefined>()
expectTypeOf(options.queryKey[dataTagSymbol]).toEqualTypeOf<
InfiniteData<string>
>()
})

it('accepts an initializer that may return undefined', () => {
const createOptions = (initialData?: InfiniteData<string, number>) =>
infiniteQueryOptions({
...queryOptions,
initialData: () => initialData,
})

const { data } = useInfiniteQuery(createOptions())

expectTypeOf(data.value).toEqualTypeOf<InfiniteData<string> | undefined>()
})

it('preserves select inference when initial data is optional', () => {
const createOptions = (initialData?: InfiniteData<string, number>) =>
infiniteQueryOptions({
...queryOptions,
initialData,
select: (data) => {
expectTypeOf(data).toEqualTypeOf<InfiniteData<string, number>>()
return data.pages.length
},
})

const { data } = useInfiniteQuery(createOptions())

expectTypeOf(data.value).toEqualTypeOf<number | undefined>()
})

it('keeps the defined overload for a guaranteed initial value', () => {
const options = infiniteQueryOptions({
...queryOptions,
initialData: { pages: ['first'], pageParams: [0] },
})

expectTypeOf(options).toExtend<
DefinedInitialDataInfiniteOptions<
string,
Error,
InfiniteData<string>,
Array<string>,
number
>
>()
})

it('keeps the defined overload for a guaranteed initializer', () => {
const options = infiniteQueryOptions({
...queryOptions,
initialData: () => ({ pages: ['first'], pageParams: [0] }),
})

expectTypeOf(options).toExtend<
DefinedInitialDataInfiniteOptions<
string,
Error,
InfiniteData<string>,
Array<string>,
number
>
>()
})

it('still accepts missing and explicitly undefined initial data', () => {
const absent = infiniteQueryOptions(queryOptions)
const explicit = infiniteQueryOptions({
...queryOptions,
initialData: undefined,
})

expectTypeOf(useInfiniteQuery(absent).data.value).toEqualTypeOf<
InfiniteData<string> | undefined
>()
expectTypeOf(useInfiniteQuery(explicit).data.value).toEqualTypeOf<
InfiniteData<string> | undefined
>()
})
})
46 changes: 26 additions & 20 deletions packages/vue-query/src/infiniteQueryOptions.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import type {
DefaultError,
InfiniteData,
InitialDataFunction,
NonUndefinedGuard,
QueryKey,
QueryKeyWithDataTag,
} from '@tanstack/query-core'
import type { UseInfiniteQueryOptions } from './useInfiniteQuery'

/**
* The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set β€” `data`
* may be `undefined` while the query is `pending`.
* The options accepted by the `infiniteQueryOptions` overload selected when `initialData` may be
* `undefined` β€” `data` may be `undefined` while the query is `pending`.
*
* @template TQueryFnData - The type of a single page, as your `queryFn` resolves it.
* @template TError - The type of errors your `queryFn` may throw.
Expand All @@ -31,12 +32,17 @@ export type UndefinedInitialDataInfiniteOptions<
TQueryKey,
TPageParam
> & {
initialData?: undefined
initialData?:
| undefined
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
| InitialDataFunction<
NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
>
}

/**
* The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set β€” `data` is
* never `undefined`.
* The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is statically
* guaranteed to produce defined data β€” `data` is never `undefined`.
*
* @template TQueryFnData - The type of a single page, as your `queryFn` resolves it.
* @template TError - The type of errors your `queryFn` may throw.
Expand Down Expand Up @@ -75,9 +81,11 @@ export type DefinedInitialDataInfiniteOptions<
* These options can be shared across hooks and imperative APIs such as `queryClient.infiniteQuery`.
* `options.queryKey` is required and is the query key to generate options for.
*
* This overload is selected when `initialData` is statically guaranteed to produce defined data, so the resulting `data` is never `undefined`.
*
* @see {@link useInfiniteQuery} to run an infinite query with these options.
* @param options - The {@link UndefinedInitialDataInfiniteOptions} to use β€” everything you can pass to
* `useInfiniteQuery`.
* @param options - The {@link DefinedInitialDataInfiniteOptions} to use β€” everything you can pass to
* `useInfiniteQuery`, with `initialData` statically guaranteed to produce defined data.
* @returns The same options object, typed so that `queryKey` carries the inferred data type.
*
* @example
Expand All @@ -90,9 +98,12 @@ export type DefinedInitialDataInfiniteOptions<
* queryFn: ({ pageParam }) => fetchProjects(pageParam),
* initialPageParam: 0,
* getNextPageParam: (lastPage) => lastPage.nextId,
* initialData: { pages: [], pageParams: [] },
* })
*
* const { data, isError, error, fetchNextPage } = useInfiniteQuery(projectsOptions)
* // `data` is never `undefined`, thanks to `initialData` β€” even if a refetch fails, so the
* // list stays visible alongside the error.
* const { data, isError, error } = useInfiniteQuery(projectsOptions)
* </script>
* ```
*/
Expand All @@ -103,14 +114,14 @@ export function infiniteQueryOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UndefinedInitialDataInfiniteOptions<
options: DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): UndefinedInitialDataInfiniteOptions<
): DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
Expand All @@ -124,11 +135,9 @@ export function infiniteQueryOptions<
* These options can be shared across hooks and imperative APIs such as `queryClient.infiniteQuery`.
* `options.queryKey` is required and is the query key to generate options for.
*
* This overload is selected when `initialData` is set, so the resulting `data` is never `undefined`.
*
* @see {@link useInfiniteQuery} to run an infinite query with these options.
* @param options - The {@link DefinedInitialDataInfiniteOptions} to use β€” everything you can pass to
* `useInfiniteQuery`, with `initialData` set.
* @param options - The {@link UndefinedInitialDataInfiniteOptions} to use β€” everything you can pass to
* `useInfiniteQuery`.
* @returns The same options object, typed so that `queryKey` carries the inferred data type.
*
* @example
Expand All @@ -141,12 +150,9 @@ export function infiniteQueryOptions<
* queryFn: ({ pageParam }) => fetchProjects(pageParam),
* initialPageParam: 0,
* getNextPageParam: (lastPage) => lastPage.nextId,
* initialData: { pages: [], pageParams: [] },
* })
*
* // `data` is never `undefined`, thanks to `initialData` β€” even if a refetch fails, so the
* // list stays visible alongside the error.
* const { data, isError, error } = useInfiniteQuery(projectsOptions)
* const { data, isError, error, fetchNextPage } = useInfiniteQuery(projectsOptions)
* </script>
* ```
*/
Expand All @@ -157,14 +163,14 @@ export function infiniteQueryOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: DefinedInitialDataInfiniteOptions<
options: UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): DefinedInitialDataInfiniteOptions<
): UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
Expand Down