import type {
DefaultError,
InfiniteData,
InitialDataFunction,
NonUndefinedGuard,
OmitKeyof,
QueryKey,
QueryKeyWithDataTag,
SkipToken,
} from '@tanstack/query-core'
import type { CreateInfiniteQueryOptions } from './types'
* The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set — `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.
* @template TData - The type `data` ends up as after `select` runs — defaults to `InfiniteData<TQueryFnData>`,
* the shape of all fetched pages plus their page params.
* @template TQueryKey - The type of your `queryKey`.
* @template TPageParam - The type of the parameter passed to `queryFn` to fetch a given page.
*/
export type UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> = CreateInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> & {
* If set, this value will be used as the initial data for the query cache (as long as the query hasn't been
* created or cached yet). If set to a function, the function will be called **once** during the shared/root
* query initialization, and be expected to synchronously return the initial data. Initial data is
* considered stale by default unless a `staleTime` has been set. `initialData` **is persisted** to the
* cache.
*/
initialData?:
| undefined
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
| InitialDataFunction<
NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
>
}
* The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set and
* `queryFn` is not `skipToken` — same as {@link UndefinedInitialDataInfiniteOptions}, but `queryFn` may not be
* `skipToken`.
*
* @template TQueryFnData - The type of a single page, as your `queryFn` resolves it.
* @template TError - The type of errors your `queryFn` may throw.
* @template TData - The type `data` ends up as after `select` runs — defaults to `InfiniteData<TQueryFnData>`,
* the shape of all fetched pages plus their page params.
* @template TQueryKey - The type of your `queryKey`.
* @template TPageParam - The type of the parameter passed to `queryFn` to fetch a given page.
*/
export type UnusedSkipTokenInfiniteOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> = OmitKeyof<
CreateInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
'queryFn'
> & {
* `skipToken` is not allowed as a value here — this overload is selected when no `initialData` is set. If
* you don't intend to run the query yet, set `enabled: false` — omitting `queryFn` alone still triggers a
* fetch that fails with "Missing queryFn" unless `enabled` is `false` or a default query function has been
* defined. A default query function only supplies `queryFn`; it doesn't defer the fetch on its own.
*/
queryFn?: Exclude<
CreateInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>['queryFn'],
SkipToken | undefined
>
}
* The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set — `data` is
* never `undefined` (unless a `select` changes `TData` to include `undefined`).
*
* @template TQueryFnData - The type of a single page, as your `queryFn` resolves it.
* @template TError - The type of errors your `queryFn` may throw.
* @template TData - The type `data` ends up as after `select` runs — defaults to `InfiniteData<TQueryFnData>`,
* the shape of all fetched pages plus their page params.
* @template TQueryKey - The type of your `queryKey`.
* @template TPageParam - The type of the parameter passed to `queryFn` to fetch a given page.
*/
export type DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> = CreateInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> & {
* If set, this value will be used as the initial data for the query cache (as long as the query hasn't been
* created or cached yet). If set to a function, the function will be called **once** during the shared/root
* query initialization, and be expected to synchronously return the initial data. Initial data is
* considered stale by default unless a `staleTime` has been set. `initialData` **is persisted** to the
* cache.
*/
initialData:
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
| (() => NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>)
| undefined
}
* You can generally pass everything to `infiniteQueryOptions` that you can also pass to
* `injectInfiniteQuery`. These options can be shared across functions and imperative APIs such as
* `queryClient.fetchInfiniteQuery`. `options.queryKey` is required and is the query key to generate options
* for.
*
* This overload is selected when `initialData` is set.
*
* @see {@link injectInfiniteQuery} to run an infinite query with these options.
* @param options - The {@link DefinedInitialDataInfiniteOptions} to use — everything you can pass to
* `injectInfiniteQuery`, with `initialData` set.
* @returns The same options object, typed so that `queryKey` carries the inferred data type.
* @remarks See {@link injectInfiniteQuery} for examples that fetch further pages, from a button click or
* automatically as the user scrolls.
*
* @example
* ```angular-ts
* import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query-experimental'
*
* export const projectsOptions = infiniteQueryOptions({
* queryKey: ['projects'],
* queryFn: ({ pageParam }) => fetchProjects(pageParam),
* initialPageParam: 0,
* getNextPageParam: (lastPage) => lastPage.nextId,
* initialData: { pages: [], pageParams: [] },
* })
*
* @Component({
* selector: 'projects',
* template: `
* <!-- `projectsQuery.data()` is never `undefined`, thanks to `initialData` — even if a
* refetch fails, so the list stays visible alongside the error. -->
* <ul>
* @for (page of projectsQuery.data().pages; track $index) {
* @for (project of page.projects; track project.id) {
* <li>{{ project.name }}</li>
* }
* }
* </ul>
* `,
* })
* export class Projects {
* readonly projectsQuery = injectInfiniteQuery(() => projectsOptions)
* }
* ```
*/
export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> &
QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
* You can generally pass everything to `infiniteQueryOptions` that you can also pass to
* `injectInfiniteQuery`. These options can be shared across functions and imperative APIs such as
* `queryClient.fetchInfiniteQuery`. `options.queryKey` is required and is the query key to generate options
* for.
*
* @returns The same options object, typed so that `queryKey` carries the inferred data type.
* @remarks See {@link injectInfiniteQuery} for examples that fetch further pages, from a button click or
* automatically as the user scrolls.
*
* @example
* A parameterized factory, so the same options object can be reused per `postId`:
* ```angular-ts
* import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query-experimental'
*
* export const commentsOptions = (postId: string) =>
* infiniteQueryOptions({
* queryKey: ['post', postId, 'comments'],
* queryFn: ({ pageParam }) => fetchComments(postId, pageParam),
* initialPageParam: 0,
* getNextPageParam: (lastPage) => lastPage.nextId,
* })
*
* @Component({
* selector: 'comments',
* template: `
* @if (commentsQuery.isPending()) {
* Loading...
* } @else if (commentsQuery.isError()) {
* <span>Error: {{ commentsQuery.error()?.message }}</span>
* } @else {
* <ul>
* @for (page of commentsQuery.data().pages; track $index) {
* @for (comment of page.comments; track comment.id) {
* <li>{{ comment.text }}</li>
* }
* }
* </ul>
* }
* `,
* })
* export class Comments {
* readonly postId = signal('1')
* readonly commentsQuery = injectInfiniteQuery(() => commentsOptions(this.postId()))
* }
* ```
*
* @see {@link injectInfiniteQuery} to run an infinite query with these options.
* @param options - The {@link UnusedSkipTokenInfiniteOptions} to use — everything you can pass to
* `injectInfiniteQuery`.
*/
export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UnusedSkipTokenInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): UnusedSkipTokenInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> &
QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
* You can generally pass everything to `infiniteQueryOptions` that you can also pass to
* `injectInfiniteQuery`. These options can be shared across functions and imperative APIs such as
* `queryClient.fetchInfiniteQuery`. `options.queryKey` is required and is the query key to generate options
* for.
*
* @returns The same options object, typed so that `queryKey` carries the inferred data type.
* @remarks See {@link injectInfiniteQuery} for examples that fetch further pages (from a button click or
* automatically as the user scrolls) and that use `skipToken` to disable the query until `postId` is set.
*
* @example
* A parameterized factory, so the same options object can be reused per `postId`:
* ```angular-ts
* import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query-experimental'
*
* export const commentsOptions = (postId: string) =>
* infiniteQueryOptions({
* queryKey: ['post', postId, 'comments'],
* queryFn: ({ pageParam }) => fetchComments(postId, pageParam),
* initialPageParam: 0,
* getNextPageParam: (lastPage) => lastPage.nextId,
* })
*
* @Component({
* selector: 'comments',
* template: `
* @if (commentsQuery.isPending()) {
* Loading...
* } @else if (commentsQuery.isError()) {
* <span>Error: {{ commentsQuery.error()?.message }}</span>
* } @else {
* <ul>
* @for (page of commentsQuery.data().pages; track $index) {
* @for (comment of page.comments; track comment.id) {
* <li>{{ comment.text }}</li>
* }
* }
* </ul>
* }
* `,
* })
* export class Comments {
* readonly postId = signal('1')
* readonly commentsQuery = injectInfiniteQuery(() => commentsOptions(this.postId()))
* }
* ```
*
* @see {@link injectInfiniteQuery} to run an infinite query with these options.
* @param options - The {@link UndefinedInitialDataInfiniteOptions} to use — everything you can pass to
* `injectInfiniteQuery`.
*/
export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> &
QueryKeyWithDataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
export function infiniteQueryOptions(options: unknown) {
return options
}