From 6fc89827d5babd2064b4fe61dc78d22fb3293170 Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Sun, 6 Sep 2026 00:18:30 +0900 Subject: [PATCH 1/3] docs(angular-query-experimental): add JSDoc across the package and generate reference docs with TypeDoc --- .../functions/infiniteQueryOptions.md | 187 +++++++++++-- .../functions/injectInfiniteQuery.md | 226 ++++++++++++++-- .../reference/functions/injectIsFetching.md | 45 +++- .../reference/functions/injectIsMutating.md | 28 +- .../reference/functions/injectIsRestoring.md | 11 +- .../reference/functions/injectMutation.md | 144 +++++++++- .../functions/injectMutationState.md | 60 ++++- .../reference/functions/injectQuery.md | 250 ++++++++---------- .../reference/functions/mutationOptions.md | 187 ++++++------- .../functions/provideAngularQuery.md | 2 +- .../reference/functions/provideIsRestoring.md | 10 +- .../reference/functions/provideQueryClient.md | 21 +- .../functions/provideTanStackQuery.md | 96 +++---- .../reference/functions/queryFeature.md | 6 +- .../reference/functions/queryOptions.md | 241 +++++++++++------ .../interfaces/BaseMutationNarrowing.md | 23 +- .../interfaces/BaseQueryNarrowing.md | 17 +- .../interfaces/CreateBaseQueryOptions.md | 18 +- .../interfaces/CreateInfiniteQueryOptions.md | 19 +- .../interfaces/CreateMutationOptions.md | 14 +- .../interfaces/CreateQueryOptions.md | 14 +- .../interfaces/InjectMutationStateOptions.md | 4 +- .../reference/interfaces/QueryFeature.md | 6 +- .../type-aliases/CreateBaseMutationResult.md | 17 +- .../type-aliases/CreateBaseQueryResult.md | 13 +- .../type-aliases/CreateInfiniteQueryResult.md | 11 +- .../type-aliases/CreateMutateAsyncFunction.md | 14 +- .../type-aliases/CreateMutateFunction.md | 15 +- .../type-aliases/CreateMutationResult.md | 16 +- .../type-aliases/CreateQueryResult.md | 8 +- .../DefinedCreateInfiniteQueryResult.md | 10 +- .../type-aliases/DefinedCreateQueryResult.md | 10 +- .../DefinedInitialDataInfiniteOptions.md | 22 +- .../type-aliases/DefinedInitialDataOptions.md | 23 +- .../reference/type-aliases/DevtoolsFeature.md | 2 +- .../type-aliases/PersistQueryClientFeature.md | 2 +- .../reference/type-aliases/QueriesOptions.md | 15 +- .../reference/type-aliases/QueriesResults.md | 15 +- .../reference/type-aliases/QueryFeatures.md | 2 +- .../UndefinedInitialDataInfiniteOptions.md | 22 +- .../UndefinedInitialDataOptions.md | 19 +- .../UnusedSkipTokenInfiniteOptions.md | 22 +- .../type-aliases/UnusedSkipTokenOptions.md | 18 +- .../src/infinite-query-options.ts | 212 +++++++++++++-- .../src/inject-infinite-query.ts | 207 +++++++++++++-- .../src/inject-is-fetching.ts | 42 ++- .../src/inject-is-mutating.ts | 24 +- .../src/inject-is-restoring.ts | 17 +- .../src/inject-mutation-state.ts | 64 ++++- .../src/inject-mutation.ts | 137 +++++++++- .../src/inject-queries.ts | 156 ++++++++++- .../src/inject-query.ts | 217 +++++++-------- .../src/mutation-options.ts | 127 ++++----- .../src/providers.ts | 83 +++--- .../src/query-options.ts | 243 ++++++++++++----- .../angular-query-experimental/src/types.ts | 156 +++++++++++ 56 files changed, 2726 insertions(+), 864 deletions(-) diff --git a/docs/framework/angular/reference/functions/infiniteQueryOptions.md b/docs/framework/angular/reference/functions/infiniteQueryOptions.md index a059fdf8c57..d2313e6c057 100644 --- a/docs/framework/angular/reference/functions/infiniteQueryOptions.md +++ b/docs/framework/angular/reference/functions/infiniteQueryOptions.md @@ -3,25 +3,20 @@ id: infiniteQueryOptions title: infiniteQueryOptions --- -Allows sharing and re-using infinite query options in a type-safe way. - -The `queryKey` will be tagged with the type from `queryFn`. - -## Param - -The infinite query options to tag with the type from `queryFn`. - ## Call Signature ```ts function infiniteQueryOptions(options): CreateInfiniteQueryOptions & object & QueryKeyWithDataTag, TError>; ``` -Defined in: [infinite-query-options.ts:88](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L88) +Defined in: [infinite-query-options.ts:181](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L181) -Allows sharing and re-using infinite query options in a type-safe way. +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. -The `queryKey` will be tagged with the type from `queryFn`. +This overload is selected when `initialData` is set. ### Type Parameters @@ -51,13 +46,53 @@ The `queryKey` will be tagged with the type from `queryFn`. [`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -The infinite query options to tag with the type from `queryFn`. +The [DefinedInitialDataInfiniteOptions](../type-aliases/DefinedInitialDataInfiniteOptions.md) to use — everything you can pass to +`injectInfiniteQuery`, with `initialData` set. ### Returns -[`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `InfiniteData`\<`TQueryFnData`, `unknown`\>, `TError`\> - -The tagged infinite query options. +The same options object, typed so that `queryKey` carries the inferred data type. + +### See + +[injectInfiniteQuery](injectInfiniteQuery.md) to run an infinite query with these options. + +### Remarks + +See [injectInfiniteQuery](injectInfiniteQuery.md) 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: ` + +
    + @for (page of projectsQuery.data().pages; track $index) { + @for (project of page.projects; track project.id) { +
  • {{ project.name }}
  • + } + } +
+ `, +}) +export class Projects { + projectsQuery = injectInfiniteQuery(() => projectsOptions) +} +``` ## Call Signature @@ -65,11 +100,12 @@ The tagged infinite query options. function infiniteQueryOptions(options): OmitKeyof, "queryFn"> & object & QueryKeyWithDataTag, TError>; ``` -Defined in: [infinite-query-options.ts:118](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L118) +Defined in: [infinite-query-options.ts:255](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L255) -Allows sharing and re-using infinite query options in a type-safe way. - -The `queryKey` will be tagged with the type from `queryFn`. +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. ### Type Parameters @@ -99,13 +135,59 @@ The `queryKey` will be tagged with the type from `queryFn`. [`UnusedSkipTokenInfiniteOptions`](../type-aliases/UnusedSkipTokenInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -The infinite query options to tag with the type from `queryFn`. +The [UnusedSkipTokenInfiniteOptions](../type-aliases/UnusedSkipTokenInfiniteOptions.md) to use — everything you can pass to +`injectInfiniteQuery`. ### Returns -`OmitKeyof`\<[`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"queryFn"`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `InfiniteData`\<`TQueryFnData`, `unknown`\>, `TError`\> +The same options object, typed so that `queryKey` carries the inferred data type. + +### Remarks + +See [injectInfiniteQuery](injectInfiniteQuery.md) 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()) { + Error: {{ commentsQuery.error()?.message }} + } @else { +
    + @for (page of commentsQuery.data().pages; track $index) { + @for (comment of page.comments; track comment.id) { +
  • {{ comment.text }}
  • + } + } +
+ } + `, +}) +export class Comments { + postId = signal('1') + commentsQuery = injectInfiniteQuery(() => commentsOptions(this.postId())) +} +``` + +### See -The tagged infinite query options. +[injectInfiniteQuery](injectInfiniteQuery.md) to run an infinite query with these options. ## Call Signature @@ -113,11 +195,12 @@ The tagged infinite query options. function infiniteQueryOptions(options): CreateInfiniteQueryOptions & object & QueryKeyWithDataTag, TError>; ``` -Defined in: [infinite-query-options.ts:148](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L148) +Defined in: [infinite-query-options.ts:329](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L329) -Allows sharing and re-using infinite query options in a type-safe way. - -The `queryKey` will be tagged with the type from `queryFn`. +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. ### Type Parameters @@ -147,10 +230,56 @@ The `queryKey` will be tagged with the type from `queryFn`. [`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -The infinite query options to tag with the type from `queryFn`. +The [UndefinedInitialDataInfiniteOptions](../type-aliases/UndefinedInitialDataInfiniteOptions.md) to use — everything you can pass to +`injectInfiniteQuery`. ### Returns -[`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `InfiniteData`\<`TQueryFnData`, `unknown`\>, `TError`\> +The same options object, typed so that `queryKey` carries the inferred data type. + +### Remarks + +See [injectInfiniteQuery](injectInfiniteQuery.md) 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()) { + Error: {{ commentsQuery.error()?.message }} + } @else { +
    + @for (page of commentsQuery.data().pages; track $index) { + @for (comment of page.comments; track comment.id) { +
  • {{ comment.text }}
  • + } + } +
+ } + `, +}) +export class Comments { + postId = signal('1') + commentsQuery = injectInfiniteQuery(() => commentsOptions(this.postId())) +} +``` + +### See -The tagged infinite query options. +[injectInfiniteQuery](injectInfiniteQuery.md) to run an infinite query with these options. diff --git a/docs/framework/angular/reference/functions/injectInfiniteQuery.md b/docs/framework/angular/reference/functions/injectInfiniteQuery.md index 17949c8d803..f5d87075b2a 100644 --- a/docs/framework/angular/reference/functions/injectInfiniteQuery.md +++ b/docs/framework/angular/reference/functions/injectInfiniteQuery.md @@ -3,27 +3,20 @@ id: injectInfiniteQuery title: injectInfiniteQuery --- -Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. -Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - -## Param - -A function that returns infinite query options. - -## Param - -Additional configuration. - ## Call Signature ```ts function injectInfiniteQuery(injectInfiniteQueryFn, options?): DefinedCreateInfiniteQueryResult; ``` -Defined in: [inject-infinite-query.ts:41](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L41) +Defined in: [inject-infinite-query.ts:83](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L83) -Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. -Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" +The options for `injectInfiniteQuery` are identical to `injectQuery`, with the addition of +`initialPageParam`, `getNextPageParam`, `getPreviousPageParam`, and `maxPages`. Infinite queries can +additively "load more" data onto an existing set of data, or "infinite scroll". + +This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, +so the resulting `data` signal is never `undefined`. ### Type Parameters @@ -53,7 +46,9 @@ Infinite queries can additively "load more" data onto an existing set of data or () => [`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -A function that returns infinite query options. +A function returning the [DefinedInitialDataInfiniteOptions](../type-aliases/DefinedInitialDataInfiniteOptions.md) to use — +everything you can pass to `injectInfiniteQuery`, with `initialData` set. Similar to `computed` from +Angular, this function runs in the reactive context, so signals read inside it drive the query. #### options? @@ -65,7 +60,49 @@ Additional configuration. [`DefinedCreateInfiniteQueryResult`](../type-aliases/DefinedCreateInfiniteQueryResult.md)\<`TData`, `TError`\> -The infinite query result. +The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, +`hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and +`data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default +`InfiniteData` shape. + +### Remarks + +Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default +refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user +actions, or add conditions like `hasNextPage() && !isFetching()`. + +### See + +[infiniteQueryOptions](infiniteQueryOptions.md) to share these options between `injectInfiniteQuery` and imperative APIs +like `queryClient.fetchInfiniteQuery`. + +### Example + +```angular-ts +@Component({ + selector: 'projects', + template: ` + +
    + @for (page of projectsQuery.data().pages; track $index) { + @for (project of page.projects; track project.id) { +
  • {{ project.name }}
  • + } + } +
+ `, +}) +export class Projects { + projectsQuery = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: ({ pageParam }) => fetchProjects(pageParam), + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage.nextId, + initialData: { pages: [], pageParams: [] }, + })) +} +``` ## Call Signature @@ -73,10 +110,11 @@ The infinite query result. function injectInfiniteQuery(injectInfiniteQueryFn, options?): CreateInfiniteQueryResult; ``` -Defined in: [inject-infinite-query.ts:65](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L65) +Defined in: [inject-infinite-query.ts:239](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L239) -Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. -Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a +unique key. Infinite queries can additively "load more" data onto an existing set of data, or +"infinite scroll". ### Type Parameters @@ -106,7 +144,9 @@ Infinite queries can additively "load more" data onto an existing set of data or () => [`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -A function that returns infinite query options. +A function returning the [UndefinedInitialDataInfiniteOptions](../type-aliases/UndefinedInitialDataInfiniteOptions.md) to use +— everything you can pass to `injectInfiniteQuery`. Similar to `computed` from Angular, this function runs +in the reactive context, so signals read inside it drive the query. #### options? @@ -118,7 +158,140 @@ Additional configuration. [`CreateInfiniteQueryResult`](../type-aliases/CreateInfiniteQueryResult.md)\<`TData`, `TError`\> -The infinite query result. +The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, +`hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and +`data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default +`InfiniteData` shape. + +### Remarks + +Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default +refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user +actions, or add conditions like `hasNextPage() && !isFetching()`. This is the only overload that accepts +`queryFn: skipToken`, shown below. + +### See + +[infiniteQueryOptions](infiniteQueryOptions.md) to share these options between `injectInfiniteQuery` and imperative APIs +like `queryClient.fetchInfiniteQuery`. + +### Examples + +Fetching the next page from a button click: +```angular-ts +@Component({ + selector: 'projects-list', + template: ` +
    + @for (page of projectsQuery.data()?.pages; track $index) { + @for (project of page.projects; track project.id) { +
  • {{ project.name }}
  • + } + } +
+ + `, +}) +export class ProjectsList { + projectsQuery = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: ({ pageParam }) => fetchProjects(pageParam), + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage.nextId, + })) +} +``` + +Fetching the next page automatically as the user scrolls, using an `IntersectionObserver` on a sentinel +element after the list: +```angular-ts +@Component({ + selector: 'projects-list', + template: ` +
    + @for (page of projectsQuery.data()?.pages; track $index) { + @for (project of page.projects; track project.id) { +
  • {{ project.name }}
  • + } + } +
+
{{ projectsQuery.isFetchingNextPage() ? 'Loading more...' : '' }}
+ `, +}) +export class ProjectsList { + sentinel = viewChild>('sentinel') + + projectsQuery = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: ({ pageParam }) => fetchProjects(pageParam), + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage.nextId, + })) + + constructor() { + effect((onCleanup) => { + const sentinel = this.sentinel()?.nativeElement + if ( + sentinel == null || + !this.projectsQuery.hasNextPage() || + this.projectsQuery.isFetching() + ) { + return + } + + const observer = new IntersectionObserver(([entry]) => { + if (entry?.isIntersecting) this.projectsQuery.fetchNextPage() + }) + observer.observe(sentinel) + + onCleanup(() => observer.disconnect()) + }) + } +} +``` + +A query that's disabled, type safe, until `postId` is set — pass `skipToken` as `queryFn` instead of +setting `enabled: false`: +```angular-ts +@Component({ + selector: 'comments', + template: ` + @if (postId() == null) { + Select a post + } @else if (commentsQuery.isPending()) { + Loading... + } @else if (commentsQuery.isError()) { + Error: {{ commentsQuery.error()?.message }} + } @else { +
    + @for (page of commentsQuery.data().pages; track $index) { + @for (comment of page.comments; track comment.id) { +
  • {{ comment.text }}
  • + } + } +
+ } + `, +}) +export class Comments { + postId = signal(undefined) + + commentsQuery = injectInfiniteQuery(() => ({ + queryKey: ['post', this.postId(), 'comments'], + queryFn: + this.postId() != null + ? ({ pageParam }) => fetchComments(this.postId()!, pageParam) + : skipToken, + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage.nextId, + })) +} +``` ## Call Signature @@ -126,10 +299,12 @@ The infinite query result. function injectInfiniteQuery(injectInfiniteQueryFn, options?): CreateInfiniteQueryResult; ``` -Defined in: [inject-infinite-query.ts:89](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L89) +Defined in: [inject-infinite-query.ts:267](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L267) -Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. -Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" +This overload accepts the general [CreateInfiniteQueryOptions](../interfaces/CreateInfiniteQueryOptions.md) shape rather than the +`initialData`-aware overloads above, so whether `data` is defined can't be inferred from the call site — +useful when wrapping `injectInfiniteQuery` in your own helper function that forwards caller-provided +options. ### Type Parameters @@ -159,7 +334,8 @@ Infinite queries can additively "load more" data onto an existing set of data or () => [`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -A function that returns infinite query options. +A function that returns infinite query options. Similar to `computed` from +Angular, this function runs in the reactive context, so signals read inside it drive the query. #### options? diff --git a/docs/framework/angular/reference/functions/injectIsFetching.md b/docs/framework/angular/reference/functions/injectIsFetching.md index 5b09a883bbb..03bc02fda52 100644 --- a/docs/framework/angular/reference/functions/injectIsFetching.md +++ b/docs/framework/angular/reference/functions/injectIsFetching.md @@ -7,12 +7,10 @@ title: injectIsFetching function injectIsFetching(filters?, options?): Signal; ``` -Defined in: [inject-is-fetching.ts:31](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-fetching.ts#L31) +Defined in: [inject-is-fetching.ts:63](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-fetching.ts#L63) -Injects a signal that tracks the number of queries that your application is loading or -fetching in the background. - -Can be used for app-wide loading indicators +Injects a signal that tracks the number of queries that your application is loading or fetching in the +background (useful for app-wide loading indicators). ## Parameters @@ -20,7 +18,7 @@ Can be used for app-wide loading indicators `QueryFilters`\ -The filters to apply to the query. +The QueryFilters to narrow down the matched queries. ### options? @@ -32,4 +30,37 @@ Additional configuration `Signal`\<`number`\> -signal with number of loading or fetching queries. +A `Signal` with the number of queries that your application is currently loading or fetching in +the background. + +## Examples + +```angular-ts +@Component({ + selector: 'posts-fetching-indicator', + template: ` + @if (isFetchingPosts()) { + Refreshing posts... + } + `, +}) +export class PostsFetchingIndicator { + // How many queries matching the posts prefix are fetching? + isFetchingPosts = injectIsFetching({ queryKey: ['posts'] }) +} +``` + +A global loading indicator for any query fetching in the background, not just the ones on screen: +```angular-ts +@Component({ + selector: 'global-loading-indicator', + template: ` + @if (isFetching()) { +
Queries are fetching in the background...
+ } + `, +}) +export class GlobalLoadingIndicator { + isFetching = injectIsFetching() +} +``` diff --git a/docs/framework/angular/reference/functions/injectIsMutating.md b/docs/framework/angular/reference/functions/injectIsMutating.md index b6935c0b988..8a5c0c27a09 100644 --- a/docs/framework/angular/reference/functions/injectIsMutating.md +++ b/docs/framework/angular/reference/functions/injectIsMutating.md @@ -7,11 +7,10 @@ title: injectIsMutating function injectIsMutating(filters?, options?): Signal; ``` -Defined in: [inject-is-mutating.ts:30](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-mutating.ts#L30) +Defined in: [inject-is-mutating.ts:46](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-mutating.ts#L46) -Injects a signal that tracks the number of mutations that your application is fetching. - -Can be used for app-wide loading indicators +Injects a signal that tracks the number of mutations that your application currently has `pending` +(useful for app-wide loading indicators). ## Parameters @@ -19,7 +18,7 @@ Can be used for app-wide loading indicators `MutationFilters`\<`unknown`, `Error`, `unknown`, `unknown`\> -The filters to apply to the query. +The MutationFilters to narrow down the matched mutations. ### options? @@ -31,4 +30,21 @@ Additional configuration `Signal`\<`number`\> -A read-only signal with the number of fetching mutations. +A `Signal` with the number of mutations that your application currently has `pending`. + +## Example + +```angular-ts +@Component({ + selector: 'posts-mutating-indicator', + template: ` + @if (isMutatingPosts()) { + Saving posts... + } + `, +}) +export class PostsMutatingIndicator { + // How many mutations matching the posts prefix are in progress? + isMutatingPosts = injectIsMutating({ mutationKey: ['posts'] }) +} +``` diff --git a/docs/framework/angular/reference/functions/injectIsRestoring.md b/docs/framework/angular/reference/functions/injectIsRestoring.md index 99cff730c21..d3b3182cafe 100644 --- a/docs/framework/angular/reference/functions/injectIsRestoring.md +++ b/docs/framework/angular/reference/functions/injectIsRestoring.md @@ -7,9 +7,11 @@ title: injectIsRestoring function injectIsRestoring(options?): Signal; ``` -Defined in: [inject-is-restoring.ts:32](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L32) +Defined in: [inject-is-restoring.ts:35](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L35) -Injects a signal that tracks whether a restore is currently in progress. [injectQuery](injectQuery.md) and friends also check this internally to avoid race conditions between the restore and initializing queries. +Injects a signal that tracks whether a restore (e.g. from a persisted client, wired up via +`provideIsRestoring`) is currently in progress. `injectQuery` and friends also check this internally to +avoid race conditions between the restore and initializing queries. ## Parameters @@ -17,10 +19,11 @@ Injects a signal that tracks whether a restore is currently in progress. [inject `InjectIsRestoringOptions` -Options for injectIsRestoring. +Additional configuration ## Returns `Signal`\<`boolean`\> -readonly signal with boolean that indicates whether a restore is in progress. +A readonly `Signal` — `true` while a restore is in progress, `false` otherwise (the +default when no `provideIsRestoring` provider is registered). diff --git a/docs/framework/angular/reference/functions/injectMutation.md b/docs/framework/angular/reference/functions/injectMutation.md index 4db9cea4725..835f0256a79 100644 --- a/docs/framework/angular/reference/functions/injectMutation.md +++ b/docs/framework/angular/reference/functions/injectMutation.md @@ -7,11 +7,10 @@ title: injectMutation function injectMutation(injectMutationFn, options?): CreateMutationResult; ``` -Defined in: [inject-mutation.ts:45](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation.ts#L45) +Defined in: [inject-mutation.ts:174](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation.ts#L174) -Injects a mutation: an imperative function that can be invoked which typically performs server side effects. - -Unlike queries, mutations are not run automatically. +Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. +`injectMutation` is the function for that. Unlike queries, mutations are not run automatically. ## Type Parameters @@ -37,7 +36,8 @@ Unlike queries, mutations are not run automatically. () => [`CreateMutationOptions`](../interfaces/CreateMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> -A function that returns mutation options. +A function that returns mutation options. Similar to `computed` from Angular, +this function runs in the reactive context, so signals read inside it drive the mutation's options. ### options? @@ -49,4 +49,136 @@ Additional configuration [`CreateMutationResult`](../type-aliases/CreateMutationResult.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> -The mutation. +The mutation result. Value fields are exposed as a `Signal` — read `data`/`error` by calling them +(e.g. `mutation.data()`) — while function fields (`mutate`, `mutateAsync`, `reset`) are called directly, +unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are type-guard methods you can call to narrow whether +`data` is defined. + +## Remarks + +`mutate`/`mutateAsync` also accept per-call `onSuccess`/`onError`/`onSettled` callbacks as a +second argument, useful for triggering call-site side effects (e.g. navigation) without coupling them to +the shared mutation definition. Callbacks defined in `injectMutationFn` fire for every mutation; per-call +callbacks fire only for the latest call you've made — `mutateAsync` gives you a promise per call instead, +so you can await `Promise.all`/`Promise.allSettled` over several calls and see each one's outcome. + +## See + +[mutationOptions](mutationOptions.md) to share these options across multiple `injectMutation` call sites, or to look +the mutation up elsewhere via its `mutationKey` (e.g. with `injectMutationState`). + +## Examples + +```angular-ts +@Component({ + selector: 'todos', + template: ` + @if (addMutation.isPending()) { + Adding todo... + } @else if (addMutation.isError()) { +
An error occurred: {{ addMutation.error()?.message }}
+ } + + `, +}) +export class Todos { + #queryClient = inject(QueryClient) + + addMutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), + })) +} +``` + +Optimistic update via `onMutate`, rolling back on `onError`: +```angular-ts +@Component({ + selector: 'todos', + template: ``, +}) +export class Todos { + #queryClient = inject(QueryClient) + + addMutation = injectMutation(() => ({ + mutationFn: addTodo, + onMutate: async (newTodo) => { + await this.#queryClient.cancelQueries({ queryKey: ['todos'] }) + const previousTodos = this.#queryClient.getQueryData>(['todos']) + + this.#queryClient.setQueryData>(['todos'], (old) => [ + ...(old ?? []), + newTodo, + ]) + + // Passed to `onError` as `onMutateResult` if the mutation fails. + return { previousTodos } + }, + onError: (_err, _newTodo, onMutateResult) => { + this.#queryClient.setQueryData(['todos'], onMutateResult?.previousTodos) + }, + onSettled: () => { + this.#queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, + })) +} +``` + +Callbacks passed per call to `mutate` only fire for the last call — `mutateAsync` gives you a promise per +call instead, so you can wait for all of them: +```angular-ts +@Component({ + selector: 'todos', + template: ` + + `, +}) +export class Todos { + #queryClient = inject(QueryClient) + + addMutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), + })) + + async handleAddAll(todos: Array) { + try { + await Promise.all(todos.map((todo) => this.addMutation.mutateAsync(todo))) + } catch (error) { + console.error('Failed to add todos:', error) + } + } +} +``` + +If some of the mutations above can fail independently of the others, and you want to know which ones did — +rather than losing that information the moment the first one rejects — swap `Promise.all` for +`Promise.allSettled`: +```angular-ts +@Component({ + selector: 'todos', + template: ` + + `, +}) +export class Todos { + #queryClient = inject(QueryClient) + + addMutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), + })) + + async handleAddAll(todos: Array) { + const addResults = await Promise.allSettled( + todos.map((todo) => this.addMutation.mutateAsync(todo)), + ) + + addResults.forEach((addResult, index) => { + if (addResult.status === 'rejected') { + console.error(`Failed to add "${todos[index]}":`, addResult.reason) + } + }) + } +} +``` diff --git a/docs/framework/angular/reference/functions/injectMutationState.md b/docs/framework/angular/reference/functions/injectMutationState.md index a101b4b5231..ae399345197 100644 --- a/docs/framework/angular/reference/functions/injectMutationState.md +++ b/docs/framework/angular/reference/functions/injectMutationState.md @@ -7,9 +7,10 @@ title: injectMutationState function injectMutationState(injectMutationStateFn, options?): Signal; ``` -Defined in: [inject-mutation-state.ts:60](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L60) +Defined in: [inject-mutation-state.ts:106](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L106) -Injects a signal that tracks the state of all mutations. +Injects a signal that gives you access to all mutations in the `MutationCache`. You can pass `filters` +(MutationFilters) to narrow down your mutations, and `select` to transform the mutation state. ## Type Parameters @@ -23,16 +24,65 @@ Injects a signal that tracks the state of all mutations. () => `MutationStateOptions`\<`TResult`\> -A function that returns mutation state options. +A function returning the `filters` to narrow down matched mutations, and an +optional `select` to transform the mutation state. Similar to `computed` from Angular, this function runs +in the reactive context, so signals read inside it re-narrow the matched mutations. ### options? [`InjectMutationStateOptions`](../interfaces/InjectMutationStateOptions.md) -The Angular injector to use. +Additional configuration ## Returns `Signal`\<`TResult`[]\> -The signal that tracks the state of all mutations. +A `Signal` with an Array of whatever `select` returns for each matching mutation. + +## Examples + +Get all variables of all running mutations: +```angular-ts +@Component({ + selector: 'pending-posts', + template: `{{ pendingVariables().length }} posts saving...`, +}) +export class PendingPosts { + pendingVariables = injectMutationState(() => ({ + filters: { status: 'pending' }, + select: (mutation) => mutation.state.variables, + })) +} +``` + +Get all data for specific mutations via the `mutationKey`: +```angular-ts +const mutationKey = ['posts'] + +@Component({ + selector: 'posts', + template: ` + + `, +}) +export class Posts { + // Some mutation that we want to get the state for + createPostMutation = injectMutation(() => ({ + mutationKey, + mutationFn: createPosts, + })) + + savedPosts = injectMutationState(() => ({ + // this mutation key needs to match the mutation key of the given mutation (see above) + filters: { mutationKey, status: 'success' }, + select: (mutation) => mutation.state.data, + })) + + createPost() { + this.createPostMutation.mutate(['New Post']) + } +} +``` diff --git a/docs/framework/angular/reference/functions/injectQuery.md b/docs/framework/angular/reference/functions/injectQuery.md index bdbfb922789..d60cf1899dc 100644 --- a/docs/framework/angular/reference/functions/injectQuery.md +++ b/docs/framework/angular/reference/functions/injectQuery.md @@ -3,87 +3,16 @@ id: injectQuery title: injectQuery --- -Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - -**Basic example** -```ts -class ServiceOrComponent { - query = injectQuery(() => ({ - queryKey: ['repoData'], - queryFn: () => - this.#http.get('https://api.github.com/repos/tanstack/query'), - })) -} -``` - -Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. -In the example below, the query will be automatically enabled and executed when the filter signal changes -to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - -**Reactive example** -```ts -class ServiceOrComponent { - filter = signal('') - - todosQuery = injectQuery(() => ({ - queryKey: ['todos', this.filter()], - queryFn: () => fetchTodos(this.filter()), - // Signals can be combined with expressions - enabled: !!this.filter(), - })) -} -``` - -## Param - -A function that returns query options. - -## Param - -Additional configuration - -## See - -https://tanstack.com/query/latest/docs/framework/angular/guides/queries - ## Call Signature ```ts function injectQuery(injectQueryFn, options?): DefinedCreateQueryResult; ``` -Defined in: [inject-query.ts:65](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L65) - -Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - -**Basic example** -```ts -class ServiceOrComponent { - query = injectQuery(() => ({ - queryKey: ['repoData'], - queryFn: () => - this.#http.get('https://api.github.com/repos/tanstack/query'), - })) -} -``` +Defined in: [inject-query.ts:68](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L68) -Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. -In the example below, the query will be automatically enabled and executed when the filter signal changes -to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - -**Reactive example** -```ts -class ServiceOrComponent { - filter = signal('') - - todosQuery = injectQuery(() => ({ - queryKey: ['todos', this.filter()], - queryFn: () => fetchTodos(this.filter()), - // Signals can be combined with expressions - enabled: !!this.filter(), - })) -} -``` +This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the +resulting `data` signal is never `undefined`. ### Type Parameters @@ -109,7 +38,9 @@ class ServiceOrComponent { () => [`DefinedInitialDataOptions`](../type-aliases/DefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -A function that returns query options. +A function returning the [DefinedInitialDataOptions](../type-aliases/DefinedInitialDataOptions.md) to use — everything you +can pass to `injectQuery`, with `initialData` set. Similar to `computed` from Angular, this function runs +in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. #### options? @@ -121,11 +52,40 @@ Additional configuration [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`TData`, `TError`\> -The query result. +The query result, typed so that `data` is never `undefined`. ### See -https://tanstack.com/query/latest/docs/framework/angular/guides/queries + - https://tanstack.com/query/latest/docs/framework/angular/guides/queries + - [queryOptions](queryOptions.md) to share these options between `injectQuery` and imperative APIs like +`queryClient.fetchQuery`. + +### Example + +```angular-ts +@Component({ + selector: 'posts', + template: ` + + @if (postsQuery.isError()) { + Error: {{ postsQuery.error()?.message }} + } +
    + @for (post of postsQuery.data(); track post.id) { +
  • {{ post.title }}
  • + } +
+ `, +}) +export class Posts { + postsQuery = injectQuery(() => ({ + queryKey: ['posts'], + queryFn: fetchPosts, + initialData: [], + })) +} +``` ## Call Signature @@ -133,39 +93,10 @@ https://tanstack.com/query/latest/docs/framework/angular/guides/queries function injectQuery(injectQueryFn, options?): CreateQueryResult; ``` -Defined in: [inject-query.ts:116](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L116) +Defined in: [inject-query.ts:157](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L157) Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. -**Basic example** -```ts -class ServiceOrComponent { - query = injectQuery(() => ({ - queryKey: ['repoData'], - queryFn: () => - this.#http.get('https://api.github.com/repos/tanstack/query'), - })) -} -``` - -Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. -In the example below, the query will be automatically enabled and executed when the filter signal changes -to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - -**Reactive example** -```ts -class ServiceOrComponent { - filter = signal('') - - todosQuery = injectQuery(() => ({ - queryKey: ['todos', this.filter()], - queryFn: () => fetchTodos(this.filter()), - // Signals can be combined with expressions - enabled: !!this.filter(), - })) -} -``` - ### Type Parameters #### TQueryFnData @@ -190,7 +121,9 @@ class ServiceOrComponent { () => [`UndefinedInitialDataOptions`](../type-aliases/UndefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -A function that returns query options. +A function returning the [UndefinedInitialDataOptions](../type-aliases/UndefinedInitialDataOptions.md) to use — everything +you can pass to `injectQuery`. Similar to `computed` from Angular, this function runs in the reactive +context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. #### options? @@ -202,51 +135,88 @@ Additional configuration [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<`TData`, `TError`\> -The query result. +The query result. `status()` is `'pending'` if there is no cached data to display, `'error'` if +the last fetch attempt failed, or `'success'` if the query has data to display. `isPending`/`isSuccess`/ +`isError` are type-guard methods for convenience. ### See -https://tanstack.com/query/latest/docs/framework/angular/guides/queries - -## Call Signature - -```ts -function injectQuery(injectQueryFn, options?): CreateQueryResult; -``` - -Defined in: [inject-query.ts:167](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L167) - -Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - -**Basic example** -```ts -class ServiceOrComponent { - query = injectQuery(() => ({ - queryKey: ['repoData'], - queryFn: () => - this.#http.get('https://api.github.com/repos/tanstack/query'), + - https://tanstack.com/query/latest/docs/framework/angular/guides/queries + - [queryOptions](queryOptions.md) to share these options between `injectQuery` and imperative APIs like +`queryClient.fetchQuery`. + +### Examples + +```angular-ts +@Component({ + selector: 'posts', + template: ` + @if (postsQuery.isPending()) { + Loading... + } @else if (postsQuery.isError()) { + Error: {{ postsQuery.error()?.message }} + } @else { +
    + @for (post of postsQuery.data(); track post.id) { +
  • {{ post.title }}
  • + } +
+ } + `, +}) +export class Posts { + postsQuery = injectQuery(() => ({ + queryKey: ['posts'], + queryFn: fetchPosts, })) } ``` -Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. -In the example below, the query will be automatically enabled and executed when the filter signal changes -to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - -**Reactive example** -```ts -class ServiceOrComponent { +Similar to `computed` from Angular, the function passed to `injectQuery` runs in the reactive context. In +the example below, the query is automatically enabled and executed when the filter signal changes to a +truthy value. When the filter signal changes back to a falsy value, the query is disabled. +```angular-ts +@Component({ + selector: 'posts', + template: ` + + @if (postsQuery.isPending()) { + Loading... + } @else if (postsQuery.isError()) { + Error: {{ postsQuery.error()?.message }} + } @else { +
    + @for (post of postsQuery.data(); track post.id) { +
  • {{ post.title }}
  • + } +
+ } + `, +}) +export class Posts { filter = signal('') - todosQuery = injectQuery(() => ({ - queryKey: ['todos', this.filter()], - queryFn: () => fetchTodos(this.filter()), + postsQuery = injectQuery(() => ({ + queryKey: ['posts', this.filter()], + queryFn: () => fetchPosts(this.filter()), // Signals can be combined with expressions enabled: !!this.filter(), })) } ``` +## Call Signature + +```ts +function injectQuery(injectQueryFn, options?): CreateQueryResult; +``` + +Defined in: [inject-query.ts:184](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L184) + +This overload accepts the general [CreateQueryOptions](../interfaces/CreateQueryOptions.md) shape rather than the `initialData`-aware +overloads above, so whether `data` is defined can't be inferred from the call site — useful when wrapping +`injectQuery` in your own helper function that forwards caller-provided options. + ### Type Parameters #### TQueryFnData @@ -271,7 +241,9 @@ class ServiceOrComponent { () => [`CreateQueryOptions`](../interfaces/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -A function that returns query options. +A function that returns query options. Similar to `computed` from Angular, this +function runs in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive +the query. #### options? diff --git a/docs/framework/angular/reference/functions/mutationOptions.md b/docs/framework/angular/reference/functions/mutationOptions.md index 647fda91bdf..04d936aae1a 100644 --- a/docs/framework/angular/reference/functions/mutationOptions.md +++ b/docs/framework/angular/reference/functions/mutationOptions.md @@ -3,81 +3,17 @@ id: mutationOptions title: mutationOptions --- -Allows sharing and re-using mutation options in a type-safe way. - -**Example** - -```ts -export class QueriesService { - private http = inject(HttpClient) - private queryClient = inject(QueryClient) - - updatePost(id: number) { - return mutationOptions({ - mutationFn: (post: Post) => Promise.resolve(post), - mutationKey: ["updatePost", id], - onSuccess: (newPost) => { - // ^? newPost: Post - this.queryClient.setQueryData(["posts", id], newPost) - }, - }); - } -} - -class ComponentOrService { - queries = inject(QueriesService) - id = signal(0) - mutation = injectMutation(() => this.queries.updatePost(this.id())) - - save() { - this.mutation.mutate({ title: 'New Title' }) - } -} -``` - -## Param - -The mutation options. - ## Call Signature ```ts function mutationOptions(options): WithRequired, "mutationKey">; ``` -Defined in: [mutation-options.ts:39](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/mutation-options.ts#L39) +Defined in: [mutation-options.ts:40](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/mutation-options.ts#L40) -Allows sharing and re-using mutation options in a type-safe way. - -**Example** - -```ts -export class QueriesService { - private http = inject(HttpClient) - private queryClient = inject(QueryClient) - - updatePost(id: number) { - return mutationOptions({ - mutationFn: (post: Post) => Promise.resolve(post), - mutationKey: ["updatePost", id], - onSuccess: (newPost) => { - // ^? newPost: Post - this.queryClient.setQueryData(["posts", id], newPost) - }, - }); - } -} - -class ComponentOrService { - queries = inject(QueriesService) - id = signal(0) - mutation = injectMutation(() => this.queries.updatePost(this.id())) - - save() { - this.mutation.mutate({ title: 'New Title' }) - } -} -``` +You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. A +`mutationKey` is required on this overload so the mutation can be looked up later, e.g. with +`injectMutationState`. ### Type Parameters @@ -103,54 +39,59 @@ class ComponentOrService { `WithRequired`\<[`CreateMutationOptions`](../interfaces/CreateMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> -The mutation options. +The mutation options to use, identical to what you'd pass to `injectMutation`, with a +required `mutationKey`. ### Returns `WithRequired`\<[`CreateMutationOptions`](../interfaces/CreateMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> -Mutation options. +The same options object, unchanged. -## Call Signature +### See -```ts -function mutationOptions(options): Omit, "mutationKey">; -``` +[injectMutation](injectMutation.md) to run the mutation these options describe. -Defined in: [mutation-options.ts:53](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/mutation-options.ts#L53) +### Example -Allows sharing and re-using mutation options in a type-safe way. +Looking the mutation up elsewhere via its `mutationKey`, e.g. for a global "saving…" indicator: +```angular-ts +import { mutationOptions, injectMutationState } from '@tanstack/angular-query-experimental' -**Example** +const createPostOptions = mutationOptions({ + mutationKey: ['posts', 'create'], + mutationFn: createPost, +}) -```ts -export class QueriesService { - private http = inject(HttpClient) - private queryClient = inject(QueryClient) - - updatePost(id: number) { - return mutationOptions({ - mutationFn: (post: Post) => Promise.resolve(post), - mutationKey: ["updatePost", id], - onSuccess: (newPost) => { - // ^? newPost: Post - this.queryClient.setQueryData(["posts", id], newPost) - }, - }); - } +@Component({ + selector: 'saving-indicator', + template: ` + @if (isCreatingPost()) { + Saving… + } + `, +}) +export class SavingIndicator { + #pendingCreates = injectMutationState(() => ({ + filters: { mutationKey: createPostOptions.mutationKey, status: 'pending' }, + })) + isCreatingPost = computed(() => this.#pendingCreates().length > 0) } +``` -class ComponentOrService { - queries = inject(QueriesService) - id = signal(0) - mutation = injectMutation(() => this.queries.updatePost(this.id())) +## Call Signature - save() { - this.mutation.mutate({ title: 'New Title' }) - } -} +```ts +function mutationOptions(options): Omit, "mutationKey">; ``` +Defined in: [mutation-options.ts:98](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/mutation-options.ts#L98) + +You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. No +`mutationKey` is required on this overload — use this when you don't need to target the mutation via a +`mutationKey` filter later (e.g. with `injectMutationState`); it can still be observed through other +filters, such as `status`. + ### Type Parameters #### TData @@ -175,10 +116,52 @@ class ComponentOrService { `Omit`\<[`CreateMutationOptions`](../interfaces/CreateMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> -The mutation options. +The mutation options to use, identical to what you'd pass to `injectMutation`, without a +`mutationKey`. ### Returns `Omit`\<[`CreateMutationOptions`](../interfaces/CreateMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"mutationKey"`\> -Mutation options. +The same options object, unchanged. + +### See + +[injectMutation](injectMutation.md) to run the mutation these options describe. + +### Remarks + +See the other overload's example for looking a mutation up via `injectMutationState`. + +### Example + +Sharing options across services, so `QueriesService` stays the single place a mutation is defined: +```angular-ts +import { mutationOptions, injectMutation } from '@tanstack/angular-query-experimental' + +@Injectable({ providedIn: 'root' }) +export class QueriesService { + #queryClient = inject(QueryClient) + + updatePost(id: number) { + return mutationOptions({ + mutationFn: (post: Partial) => putPost(id, post), + onSuccess: (newPost) => this.#queryClient.setQueryData(['posts', id], newPost), + }) + } +} + +@Component({ + selector: 'post', + template: ``, +}) +export class Post { + queries = inject(QueriesService) + id = signal(0) + updatePostMutation = injectMutation(() => this.queries.updatePost(this.id())) + + save() { + this.updatePostMutation.mutate({ title: 'New Title' }) + } +} +``` diff --git a/docs/framework/angular/reference/functions/provideAngularQuery.md b/docs/framework/angular/reference/functions/provideAngularQuery.md index 6f73f940844..0efb348b8a0 100644 --- a/docs/framework/angular/reference/functions/provideAngularQuery.md +++ b/docs/framework/angular/reference/functions/provideAngularQuery.md @@ -7,7 +7,7 @@ title: provideAngularQuery function provideAngularQuery(queryClient): Provider[]; ``` -Defined in: [providers.ts:124](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L124) +Defined in: [providers.ts:121](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L121) Sets up providers necessary to enable TanStack Query functionality for Angular applications. diff --git a/docs/framework/angular/reference/functions/provideIsRestoring.md b/docs/framework/angular/reference/functions/provideIsRestoring.md index e3112e03a32..e33a3e479ae 100644 --- a/docs/framework/angular/reference/functions/provideIsRestoring.md +++ b/docs/framework/angular/reference/functions/provideIsRestoring.md @@ -7,9 +7,11 @@ title: provideIsRestoring function provideIsRestoring(isRestoring): Provider; ``` -Defined in: [inject-is-restoring.ts:43](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L43) +Defined in: [inject-is-restoring.ts:48](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L48) -Used by TanStack Query Angular persist client plugin to provide the signal that tracks the restore state +Registers a provider for the restore state read by `injectIsRestoring`. Wire this up wherever you drive a +restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can hold off +initializing queries until the restore signal flips back to `false`. ## Parameters @@ -17,10 +19,10 @@ Used by TanStack Query Angular persist client plugin to provide the signal that `Signal`\<`boolean`\> -a readonly signal that returns a boolean +A readonly `Signal` that tracks the restore state. ## Returns `Provider` -Provider for the `isRestoring` signal +A provider for the `isRestoring` signal. diff --git a/docs/framework/angular/reference/functions/provideQueryClient.md b/docs/framework/angular/reference/functions/provideQueryClient.md index 1b7afdf1996..76ae784100d 100644 --- a/docs/framework/angular/reference/functions/provideQueryClient.md +++ b/docs/framework/angular/reference/functions/provideQueryClient.md @@ -7,13 +7,12 @@ title: provideQueryClient function provideQueryClient(queryClient): Provider; ``` -Defined in: [providers.ts:14](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L14) +Defined in: [providers.ts:22](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L22) Usually [provideTanStackQuery](provideTanStackQuery.md) is used once to set up TanStack Query and the -[https://tanstack.com/query/latest/docs/reference/QueryClient\|QueryClient](https://tanstack.com/query/latest/docs/reference/QueryClient|QueryClient) -for the entire application. Internally it calls `provideQueryClient`. -You can use `provideQueryClient` to provide a different `QueryClient` instance for a part -of the application or for unit testing purposes. +[`QueryClient`](https://tanstack.com/query/latest/docs/reference/QueryClient) for the entire application — +it calls `provideQueryClient` internally. Use `provideQueryClient` directly to provide a different +`QueryClient` instance for part of the application, or for unit testing. ## Parameters @@ -27,4 +26,14 @@ A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. `Provider` -a provider object that can be used to provide the `QueryClient` instance. +A provider object that can be used to provide the `QueryClient` instance. + +## Example + +Providing a test-only `QueryClient` in a component test, without wiring up `provideTanStackQuery`'s other +defaults: +```ts +TestBed.configureTestingModule({ + providers: [provideQueryClient(new QueryClient())], +}) +``` diff --git a/docs/framework/angular/reference/functions/provideTanStackQuery.md b/docs/framework/angular/reference/functions/provideTanStackQuery.md index 9914d12ed9f..e94975d12f8 100644 --- a/docs/framework/angular/reference/functions/provideTanStackQuery.md +++ b/docs/framework/angular/reference/functions/provideTanStackQuery.md @@ -7,32 +7,49 @@ title: provideTanStackQuery function provideTanStackQuery(queryClient, ...features): Provider[]; ``` -Defined in: [providers.ts:105](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L105) +Defined in: [providers.ts:102](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L102) -Sets up providers necessary to enable TanStack Query functionality for Angular applications. +Sets up providers necessary to enable TanStack Query functionality for Angular applications. Allows +configuring a `QueryClient` and optional features such as developer tools. -Allows configuring a `QueryClient` and optional features such as developer tools. +## Parameters + +### queryClient + +A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. + +`QueryClient` | `InjectionToken`\<`QueryClient`\> + +### features + +...[`QueryFeatures`](../type-aliases/QueryFeatures.md)[] + +Optional features to configure additional Query functionality. + +## Returns -**Example - standalone** +`Provider`[] + +A set of providers to set up TanStack Query. + +## See + + - https://tanstack.com/query/v5/docs/framework/angular/quick-start + - withDevtools + +## Examples ```ts -import { - provideTanStackQuery, - QueryClient, -} from '@tanstack/angular-query-experimental' +import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' bootstrapApplication(AppComponent, { providers: [provideTanStackQuery(new QueryClient())], }) ``` -**Example - NgModule-based** - +The same, in an `NgModule`-based application: ```ts -import { - provideTanStackQuery, - QueryClient, -} from '@tanstack/angular-query-experimental' +import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' @NgModule({ declarations: [AppComponent], @@ -43,26 +60,24 @@ import { export class AppModule {} ``` -You can also enable optional developer tools by adding `withDevtools`. By -default the tools will then be loaded when your app is in development mode. +Enabling optional developer tools by adding `withDevtools` — by default, the tools are then loaded when +your app is in development mode: ```ts import { provideTanStackQuery, - withDevtools + withDevtools, QueryClient, } from '@tanstack/angular-query-experimental' -bootstrapApplication(AppComponent, - { - providers: [ - provideTanStackQuery(new QueryClient(), withDevtools()) - ] - } -) +bootstrapApplication(AppComponent, { + providers: [provideTanStackQuery(new QueryClient(), withDevtools())], +}) ``` -**Example: using an InjectionToken** - +Using an `InjectionToken` for the `QueryClient` — an advanced optimization that lets TanStack Query be +absent from the main application bundle, useful for including it on lazy-loaded routes only while still +sharing a `QueryClient`. This is a small optimization; for most applications it's preferable to provide +the `QueryClient` in the main application config, as in the examples above: ```ts export const MY_QUERY_CLIENT = new InjectionToken('', { factory: () => new QueryClient(), @@ -71,32 +86,3 @@ export const MY_QUERY_CLIENT = new InjectionToken('', { // In a lazy loaded route or lazy loaded component's providers array: providers: [provideTanStackQuery(MY_QUERY_CLIENT)] ``` -Using an InjectionToken for the QueryClient is an advanced optimization which allows TanStack Query to be absent from the main application bundle. -This can be beneficial if you want to include TanStack Query on lazy loaded routes only while still sharing a `QueryClient`. - -Note that this is a small optimization and for most applications it's preferable to provide the `QueryClient` in the main application config. - -## Parameters - -### queryClient - -A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - -`QueryClient` | `InjectionToken`\<`QueryClient`\> - -### features - -...[`QueryFeatures`](../type-aliases/QueryFeatures.md)[] - -Optional features to configure additional Query functionality. - -## Returns - -`Provider`[] - -A set of providers to set up TanStack Query. - -## See - - - https://tanstack.com/query/v5/docs/framework/angular/quick-start - - withDevtools diff --git a/docs/framework/angular/reference/functions/queryFeature.md b/docs/framework/angular/reference/functions/queryFeature.md index accbb998067..86ef8b1543b 100644 --- a/docs/framework/angular/reference/functions/queryFeature.md +++ b/docs/framework/angular/reference/functions/queryFeature.md @@ -7,7 +7,7 @@ title: queryFeature function queryFeature(kind, providers): QueryFeature; ``` -Defined in: [providers.ts:146](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L146) +Defined in: [providers.ts:143](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L143) Helper function to create an object that represents a Query feature. @@ -23,10 +23,14 @@ Helper function to create an object that represents a Query feature. `TFeatureKind` +The kind of feature, e.g. `'Devtools'`. + ### providers `Provider`[] +The Angular providers this feature contributes to `provideTanStackQuery`. + ## Returns [`QueryFeature`](../interfaces/QueryFeature.md)\<`TFeatureKind`\> diff --git a/docs/framework/angular/reference/functions/queryOptions.md b/docs/framework/angular/reference/functions/queryOptions.md index 41c22b41169..f8f33e5b789 100644 --- a/docs/framework/angular/reference/functions/queryOptions.md +++ b/docs/framework/angular/reference/functions/queryOptions.md @@ -3,53 +3,19 @@ id: queryOptions title: queryOptions --- -Allows sharing and re-using query options in a type-safe way. - -The `queryKey` will be tagged with the type from `queryFn`. - -**Example** - -```ts - const { queryKey } = queryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // ^? Promise - }) - - const queryClient = new QueryClient() - const data = queryClient.getQueryData(queryKey) - // ^? number | undefined -``` - -## Param - -The query options to tag with the type from `queryFn`. - ## Call Signature ```ts function queryOptions(options): Omit, "queryFn"> & object & QueryKeyWithDataTag; ``` -Defined in: [query-options.ts:76](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L76) - -Allows sharing and re-using query options in a type-safe way. - -The `queryKey` will be tagged with the type from `queryFn`. - -**Example** +Defined in: [query-options.ts:150](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L150) -```ts - const { queryKey } = queryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // ^? Promise - }) +You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options +can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is +required and is the query key to generate options for. - const queryClient = new QueryClient() - const data = queryClient.getQueryData(queryKey) - // ^? number | undefined -``` +This overload is selected when `initialData` is set, so the resulting `data` is never `undefined`. ### Type Parameters @@ -75,13 +41,48 @@ The `queryKey` will be tagged with the type from `queryFn`. [`DefinedInitialDataOptions`](../type-aliases/DefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -The query options to tag with the type from `queryFn`. +The [DefinedInitialDataOptions](../type-aliases/DefinedInitialDataOptions.md) to use — everything you can pass to `injectQuery`, +with `initialData` set. ### Returns -`Omit`\<[`CreateQueryOptions`](../interfaces/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `TQueryFnData`, `TError`\> - -The tagged query options. +The same options object, typed so that `queryKey` carries the inferred data type. + +### See + + - [injectQuery](injectQuery.md) to run a query with these options. + - [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. + +### Example + +```angular-ts +import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' + +export const postsOptions = queryOptions({ + queryKey: ['posts'], + queryFn: fetchPosts, + initialData: [], +}) + +@Component({ + selector: 'posts', + template: ` + + @if (postsQuery.isError()) { + Error: {{ postsQuery.error()?.message }} + } +
    + @for (post of postsQuery.data(); track post.id) { +
  • {{ post.title }}
  • + } +
+ `, +}) +export class Posts { + postsQuery = injectQuery(() => postsOptions) +} +``` ## Call Signature @@ -89,25 +90,11 @@ The tagged query options. function queryOptions(options): OmitKeyof, "queryFn"> & object & QueryKeyWithDataTag; ``` -Defined in: [query-options.ts:107](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L107) - -Allows sharing and re-using query options in a type-safe way. - -The `queryKey` will be tagged with the type from `queryFn`. +Defined in: [query-options.ts:199](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L199) -**Example** - -```ts - const { queryKey } = queryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // ^? Promise - }) - - const queryClient = new QueryClient() - const data = queryClient.getQueryData(queryKey) - // ^? number | undefined -``` +You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options +can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is +required and is the query key to generate options for. ### Type Parameters @@ -133,40 +120,59 @@ The `queryKey` will be tagged with the type from `queryFn`. [`UnusedSkipTokenOptions`](../type-aliases/UnusedSkipTokenOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -The query options to tag with the type from `queryFn`. +The [UnusedSkipTokenOptions](../type-aliases/UnusedSkipTokenOptions.md) to use — everything you can pass to `injectQuery`. ### Returns -`OmitKeyof`\<[`CreateQueryOptions`](../interfaces/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `TQueryFnData`, `TError`\> +The same options object, typed so that `queryKey` carries the inferred data type. -The tagged query options. +### See -## Call Signature + - [injectQuery](injectQuery.md) to run a query with these options. + - [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. -```ts -function queryOptions(options): CreateQueryOptions & object & QueryKeyWithDataTag; -``` +### Example -Defined in: [query-options.ts:138](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L138) +A parameterized factory, so the same options object can be reused per `id`: +```angular-ts +import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' -Allows sharing and re-using query options in a type-safe way. +export const postOptions = (id: string) => + queryOptions({ + queryKey: ['post', id], + queryFn: () => fetchPost(id), + }) -The `queryKey` will be tagged with the type from `queryFn`. +@Component({ + selector: 'post', + template: ` + @if (postQuery.isPending()) { + Loading... + } @else if (postQuery.isError()) { + Error: {{ postQuery.error()?.message }} + } @else { +

{{ postQuery.data().title }}

+ } + `, +}) +export class Post { + id = signal('1') + postQuery = injectQuery(() => postOptions(this.id())) +} +``` -**Example** +## Call Signature ```ts - const { queryKey } = queryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // ^? Promise - }) - - const queryClient = new QueryClient() - const data = queryClient.getQueryData(queryKey) - // ^? number | undefined +function queryOptions(options): CreateQueryOptions & object & QueryKeyWithDataTag; ``` +Defined in: [query-options.ts:280](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L280) + +You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options +can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is +required and is the query key to generate options for. + ### Type Parameters #### TQueryFnData @@ -191,10 +197,77 @@ The `queryKey` will be tagged with the type from `queryFn`. [`UndefinedInitialDataOptions`](../type-aliases/UndefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -The query options to tag with the type from `queryFn`. +The [UndefinedInitialDataOptions](../type-aliases/UndefinedInitialDataOptions.md) to use — everything you can pass to `injectQuery`. ### Returns -[`CreateQueryOptions`](../interfaces/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `TQueryFnData`, `TError`\> +The same options object, typed so that `queryKey` carries the inferred data type. + +### See + + - [injectQuery](injectQuery.md) to run a query with these options. + - [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. -The tagged query options. +### Remarks + +This is the only overload that accepts `queryFn: skipToken`, shown below. + +### Examples + +A parameterized factory, so the same options object can be reused per `id`: +```angular-ts +import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' + +export const postOptions = (id: string) => + queryOptions({ + queryKey: ['post', id], + queryFn: () => fetchPost(id), + }) + +@Component({ + selector: 'post', + template: ` + @if (postQuery.isPending()) { + Loading... + } @else if (postQuery.isError()) { + Error: {{ postQuery.error()?.message }} + } @else { +

{{ postQuery.data().title }}

+ } + `, +}) +export class Post { + id = signal('1') + postQuery = injectQuery(() => postOptions(this.id())) +} +``` + +A factory that disables the query, type safe, until `postId` is set: +```angular-ts +import { queryOptions, skipToken, injectQuery } from '@tanstack/angular-query-experimental' + +export const postOptions = (postId: number | undefined) => + queryOptions({ + queryKey: ['post', postId], + queryFn: postId != null ? () => fetchPost(postId) : skipToken, + }) + +@Component({ + selector: 'post', + template: ` + @if (postId() == null) { + Select a post + } @else if (postQuery.isPending()) { + Loading... + } @else if (postQuery.isError()) { + Error: {{ postQuery.error()?.message }} + } @else { +

{{ postQuery.data().title }}

+ } + `, +}) +export class Post { + postId = signal(undefined) + postQuery = injectQuery(() => postOptions(this.postId())) +} +``` diff --git a/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md b/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md index 5e047f4a472..5ea77a60815 100644 --- a/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md +++ b/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md @@ -3,7 +3,11 @@ id: BaseMutationNarrowing title: BaseMutationNarrowing --- -Defined in: [types.ts:184](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L184) +Defined in: [types.ts:328](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L328) + +The `isSuccess`/`isError`/`isPending`/`isIdle` methods on a mutation result. Each is both a `Signal` +(its current boolean value is read reactively without calling it) and a type-guard function you can +call — `if (mutation.isSuccess())` — so that `mutation.data` narrows away `undefined` inside the branch. ## Type Parameters @@ -11,18 +15,27 @@ Defined in: [types.ts:184](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type your mutation function resolves to. + ### TError `TError` = `DefaultError` +The type of errors your mutation function may throw. + ### TVariables `TVariables` = `unknown` +The type of the variable passed to `mutate`/`mutateAsync`. + ### TOnMutateResult `TOnMutateResult` = `unknown` +The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as +their `onMutateResult` parameter — useful for optimistic-update rollback data. + ## Properties ### isError @@ -31,7 +44,7 @@ Defined in: [types.ts:184](https://github.com/TanStack/query/blob/main/packages/ isError: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [types.ts:207](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L207) +Defined in: [types.ts:351](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L351) *** @@ -41,7 +54,7 @@ Defined in: [types.ts:207](https://github.com/TanStack/query/blob/main/packages/ isIdle: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [types.ts:241](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L241) +Defined in: [types.ts:385](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L385) *** @@ -51,7 +64,7 @@ Defined in: [types.ts:241](https://github.com/TanStack/query/blob/main/packages/ isPending: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [types.ts:224](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L224) +Defined in: [types.ts:368](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L368) *** @@ -61,4 +74,4 @@ Defined in: [types.ts:224](https://github.com/TanStack/query/blob/main/packages/ isSuccess: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [types.ts:190](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L190) +Defined in: [types.ts:334](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L334) diff --git a/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md b/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md index feff9ecff52..eaf5decb7bf 100644 --- a/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md +++ b/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md @@ -3,7 +3,12 @@ id: BaseQueryNarrowing title: BaseQueryNarrowing --- -Defined in: [types.ts:51](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L51) +Defined in: [types.ts:83](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L83) + +The `isSuccess`/`isError`/`isPending` methods on a query result. Unlike `react-query`'s derived booleans, +these are type-guard methods you call — `if (query.isSuccess())` — so that `query.data` narrows away +`undefined` inside the branch, the same way `status` narrowing works on the plain object `react-query` +returns. ## Type Parameters @@ -11,10 +16,14 @@ Defined in: [types.ts:51](https://github.com/TanStack/query/blob/main/packages/a `TData` = `unknown` +The type `data` ends up as after `select` runs. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ## Properties ### isError() @@ -23,7 +32,7 @@ Defined in: [types.ts:51](https://github.com/TanStack/query/blob/main/packages/a isError: (this) => this is CreateBaseQueryResult>; ``` -Defined in: [types.ts:59](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L59) +Defined in: [types.ts:91](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L91) #### Parameters @@ -43,7 +52,7 @@ Defined in: [types.ts:59](https://github.com/TanStack/query/blob/main/packages/a isPending: (this) => this is CreateBaseQueryResult>; ``` -Defined in: [types.ts:66](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L66) +Defined in: [types.ts:98](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L98) #### Parameters @@ -63,7 +72,7 @@ Defined in: [types.ts:66](https://github.com/TanStack/query/blob/main/packages/a isSuccess: (this) => this is CreateBaseQueryResult>; ``` -Defined in: [types.ts:52](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L52) +Defined in: [types.ts:84](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L84) #### Parameters diff --git a/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md b/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md index 1e0ec0cbb2d..f563462798b 100644 --- a/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md +++ b/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md @@ -3,7 +3,11 @@ id: CreateBaseQueryOptions title: CreateBaseQueryOptions --- -Defined in: [types.ts:21](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L21) +Defined in: [types.ts:34](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L34) + +The options shared across `angular-query-experimental`'s query functions. Extends +QueryObserverOptions from `@tanstack/query-core` as-is — unlike `react-query`, +`angular-query-experimental` has no extra framework-specific option here. ## Extends @@ -15,18 +19,30 @@ Defined in: [types.ts:21](https://github.com/TanStack/query/blob/main/packages/a `TQueryFnData` = `unknown` +The type your `queryFn` resolves to. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `TQueryFnData` +The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no +`select` is used. + ### TQueryData `TQueryData` = `TQueryFnData` +The type of the data actually held in the query cache — the input to `select` and +`placeholderData`. Defaults to, and is usually the same as, `TQueryFnData`. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` + +The type of your `queryKey`. diff --git a/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md b/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md index 2adff660535..5dba1476e96 100644 --- a/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md +++ b/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md @@ -3,7 +3,12 @@ id: CreateInfiniteQueryOptions title: CreateInfiniteQueryOptions --- -Defined in: [types.ts:75](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L75) +Defined in: [types.ts:121](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L121) + +The options accepted by `injectInfiniteQuery`. Same as [CreateBaseQueryOptions](CreateBaseQueryOptions.md), minus `suspense` — +which `angular-query-experimental` doesn't support, unlike `react-query` — extends +InfiniteQueryObserverOptions from `@tanstack/query-core` for the infinite-query-specific options +(`getNextPageParam`, `initialPageParam`, etc.). ## Extends @@ -15,18 +20,30 @@ Defined in: [types.ts:75](https://github.com/TanStack/query/blob/main/packages/a `TQueryFnData` = `unknown` +The type of a single page, as your `queryFn` resolves it. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `TQueryFnData` +The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` here, though +`injectInfiniteQuery` itself defaults it to `InfiniteData` — the shape `data` actually has +when no `select` is used. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` +The type of your `queryKey`. + ### TPageParam `TPageParam` = `unknown` + +The type of the parameter passed to `queryFn` to fetch a given page. diff --git a/docs/framework/angular/reference/interfaces/CreateMutationOptions.md b/docs/framework/angular/reference/interfaces/CreateMutationOptions.md index ada9620a61c..91bfca38e06 100644 --- a/docs/framework/angular/reference/interfaces/CreateMutationOptions.md +++ b/docs/framework/angular/reference/interfaces/CreateMutationOptions.md @@ -3,7 +3,10 @@ id: CreateMutationOptions title: CreateMutationOptions --- -Defined in: [types.ts:126](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L126) +Defined in: [types.ts:224](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L224) + +The options accepted by `injectMutation`. Same as MutationObserverOptions from +`@tanstack/query-core`, minus the internal `_defaulted` flag. ## Extends @@ -15,14 +18,23 @@ Defined in: [types.ts:126](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type your mutation function resolves to. + ### TError `TError` = `DefaultError` +The type of errors your mutation function may throw. + ### TVariables `TVariables` = `void` +The type of the variable passed to `mutate`/`mutateAsync`. + ### TOnMutateResult `TOnMutateResult` = `unknown` + +The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as +their `onMutateResult` parameter — useful for optimistic-update rollback data. diff --git a/docs/framework/angular/reference/interfaces/CreateQueryOptions.md b/docs/framework/angular/reference/interfaces/CreateQueryOptions.md index 6476c4dfa2b..52a56d7e8fb 100644 --- a/docs/framework/angular/reference/interfaces/CreateQueryOptions.md +++ b/docs/framework/angular/reference/interfaces/CreateQueryOptions.md @@ -3,7 +3,10 @@ id: CreateQueryOptions title: CreateQueryOptions --- -Defined in: [types.ts:35](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L35) +Defined in: [types.ts:58](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L58) + +The options accepted by `injectQuery`. Same as [CreateBaseQueryOptions](CreateBaseQueryOptions.md), minus `suspense` — which +`angular-query-experimental` doesn't support, unlike `react-query`. ## Extends @@ -15,14 +18,23 @@ Defined in: [types.ts:35](https://github.com/TanStack/query/blob/main/packages/a `TQueryFnData` = `unknown` +The type your `queryFn` resolves to. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `TQueryFnData` +The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no +`select` is used. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` + +The type of your `queryKey`. diff --git a/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md b/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md index 5b2fb7d7a40..73b944e27da 100644 --- a/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md +++ b/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md @@ -3,7 +3,7 @@ id: InjectMutationStateOptions title: InjectMutationStateOptions --- -Defined in: [inject-mutation-state.ts:45](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L45) +Defined in: [inject-mutation-state.ts:40](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L40) ## Properties @@ -13,7 +13,7 @@ Defined in: [inject-mutation-state.ts:45](https://github.com/TanStack/query/blob optional injector: Injector; ``` -Defined in: [inject-mutation-state.ts:51](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L51) +Defined in: [inject-mutation-state.ts:46](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L46) The `Injector` in which to create the mutation state signal. diff --git a/docs/framework/angular/reference/interfaces/QueryFeature.md b/docs/framework/angular/reference/interfaces/QueryFeature.md index 64771848073..916e0a4659d 100644 --- a/docs/framework/angular/reference/interfaces/QueryFeature.md +++ b/docs/framework/angular/reference/interfaces/QueryFeature.md @@ -3,7 +3,7 @@ id: QueryFeature title: QueryFeature --- -Defined in: [providers.ts:135](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L135) +Defined in: [providers.ts:132](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L132) Helper type to represent a Query feature. @@ -21,7 +21,7 @@ Helper type to represent a Query feature. ɵkind: TFeatureKind; ``` -Defined in: [providers.ts:136](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L136) +Defined in: [providers.ts:133](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L133) *** @@ -31,4 +31,4 @@ Defined in: [providers.ts:136](https://github.com/TanStack/query/blob/main/packa ɵproviders: Provider[]; ``` -Defined in: [providers.ts:137](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L137) +Defined in: [providers.ts:134](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L134) diff --git a/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md b/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md index 8e9e246a081..f593d6ce62d 100644 --- a/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md @@ -9,7 +9,11 @@ type CreateBaseMutationResult = Over }> & object; ``` -Defined in: [types.ts:154](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L154) +Defined in: [types.ts:284](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L284) + +The pre-`Signal` shape [CreateMutationResult](CreateMutationResult.md) is built from — not what `injectMutation` actually +returns. Same as MutationObserverResult from `@tanstack/query-core`, with `mutate` narrowed to the +fire-and-forget [CreateMutateFunction](CreateMutateFunction.md) signature, plus the added `mutateAsync`. ## Type Declaration @@ -19,20 +23,31 @@ Defined in: [types.ts:154](https://github.com/TanStack/query/blob/main/packages/ mutateAsync: CreateMutateAsyncFunction; ``` +Similar to `mutate`, but returns a promise which can be awaited. + ## Type Parameters ### TData `TData` = `unknown` +The type your mutation function resolves to. + ### TError `TError` = `DefaultError` +The type of errors your mutation function may throw. + ### TVariables `TVariables` = `unknown` +The type of the variable passed to `mutate`/`mutateAsync`. + ### TOnMutateResult `TOnMutateResult` = `unknown` + +The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as +their `onMutateResult` parameter — useful for optimistic-update rollback data. diff --git a/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md b/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md index 67a8c8a960e..988a2074f36 100644 --- a/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md @@ -7,7 +7,14 @@ title: CreateBaseQueryResult type CreateBaseQueryResult = BaseQueryNarrowing & MapToSignals>; ``` -Defined in: [types.ts:92](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L92) +Defined in: [types.ts:149](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L149) + +The result of `injectQuery` when `initialData` isn't set — `data` may be `undefined` while the query is +`pending`. Same shape as QueryObserverResult from `@tanstack/query-core`, but value fields (like +`data`, `error`, `status`) are exposed as a `Signal` — read them with `query.data()`, not `query.data` — +while function fields (like `refetch`) are called directly, unchanged. `isSuccess`/`isError`/`isPending` +are [BaseQueryNarrowing](../interfaces/BaseQueryNarrowing.md) type-guard methods rather than plain booleans. +`injectInfiniteQuery` returns [CreateInfiniteQueryResult](CreateInfiniteQueryResult.md) instead. ## Type Parameters @@ -15,10 +22,14 @@ Defined in: [types.ts:92](https://github.com/TanStack/query/blob/main/packages/a `TData` = `unknown` +The type `data` ends up as after `select` runs. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TState `TState` = `QueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md b/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md index aacb30e9e2e..b5d12e1b9d3 100644 --- a/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md @@ -7,7 +7,12 @@ title: CreateInfiniteQueryResult type CreateInfiniteQueryResult = BaseQueryNarrowing & MapToSignals>; ``` -Defined in: [types.ts:111](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L111) +Defined in: [types.ts:191](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L191) + +The result of `injectInfiniteQuery` when `initialData` isn't set — `data` may be `undefined` while the +query is `pending`. Same shape as InfiniteQueryObserverResult from `@tanstack/query-core`, but +value fields are exposed as a `Signal` while function fields (like `fetchNextPage`) are called directly, +unchanged. ## Type Parameters @@ -15,6 +20,10 @@ Defined in: [types.ts:111](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type `data` ends up as after `select` runs. + ### TError `TError` = `DefaultError` + +The type of errors your `queryFn` may throw. diff --git a/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md b/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md index 2427e3ac109..af507a45388 100644 --- a/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md +++ b/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md @@ -7,7 +7,10 @@ title: CreateMutateAsyncFunction type CreateMutateAsyncFunction = MutateFunction; ``` -Defined in: [types.ts:147](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L147) +Defined in: [types.ts:266](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L266) + +The type of `mutateAsync`, as returned by `injectMutation`. Similar to [CreateMutateFunction](CreateMutateFunction.md), but +returns a promise which can be awaited. ## Type Parameters @@ -15,14 +18,23 @@ Defined in: [types.ts:147](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type your mutation function resolves to. + ### TError `TError` = `DefaultError` +The type of errors your mutation function may throw. + ### TVariables `TVariables` = `void` +The type of the variable passed to `mutateAsync`. + ### TOnMutateResult `TOnMutateResult` = `unknown` + +The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as +their `onMutateResult` parameter — useful for optimistic-update rollback data. diff --git a/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md b/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md index 0f23d95cca1..62ffebc6351 100644 --- a/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md +++ b/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md @@ -7,7 +7,11 @@ title: CreateMutateFunction type CreateMutateFunction = (...args) => void; ``` -Defined in: [types.ts:136](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L136) +Defined in: [types.ts:245](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L245) + +The type of `mutate`, as returned by `injectMutation`. Forwards the variables (and an optional per-call +`onSuccess`/`onError`/`onSettled`) to the underlying `mutate` call. Fire-and-forget — errors are surfaced +through the mutation result, not thrown. ## Type Parameters @@ -15,18 +19,27 @@ Defined in: [types.ts:136](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type your mutation function resolves to. + ### TError `TError` = `DefaultError` +The type of errors your mutation function may throw. + ### TVariables `TVariables` = `void` +The type of the variable passed to `mutate`. + ### TOnMutateResult `TOnMutateResult` = `unknown` +The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as +their `onMutateResult` parameter — useful for optimistic-update rollback data. + ## Parameters ### args diff --git a/docs/framework/angular/reference/type-aliases/CreateMutationResult.md b/docs/framework/angular/reference/type-aliases/CreateMutationResult.md index f057b2a179e..a355e7a1eca 100644 --- a/docs/framework/angular/reference/type-aliases/CreateMutationResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateMutationResult.md @@ -7,7 +7,12 @@ title: CreateMutationResult type CreateMutationResult = BaseMutationNarrowing & MapToSignals>; ``` -Defined in: [types.ts:260](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L260) +Defined in: [types.ts:416](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L416) + +The result of `injectMutation`. Based on [CreateBaseMutationResult](CreateBaseMutationResult.md), but value fields are exposed as +a `Signal` — read them with `mutation.data()`, not `mutation.data` — while function fields (`mutate`, +`mutateAsync`, `reset`) are called directly, unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are +[BaseMutationNarrowing](../interfaces/BaseMutationNarrowing.md) type-guard methods rather than plain booleans. ## Type Parameters @@ -15,18 +20,27 @@ Defined in: [types.ts:260](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type your mutation function resolves to. + ### TError `TError` = `DefaultError` +The type of errors your mutation function may throw. + ### TVariables `TVariables` = `unknown` +The type of the variable passed to `mutate`/`mutateAsync`. + ### TOnMutateResult `TOnMutateResult` = `unknown` +The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as +their `onMutateResult` parameter — useful for optimistic-update rollback data. + ### TState `TState` = `CreateStatusBasedMutationResult`\<[`CreateBaseMutationResult`](CreateBaseMutationResult.md)\[`"status"`\], `TData`, `TError`, `TVariables`, `TOnMutateResult`\> diff --git a/docs/framework/angular/reference/type-aliases/CreateQueryResult.md b/docs/framework/angular/reference/type-aliases/CreateQueryResult.md index ed4705d8424..c1a4fb1bc5d 100644 --- a/docs/framework/angular/reference/type-aliases/CreateQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateQueryResult.md @@ -7,7 +7,9 @@ title: CreateQueryResult type CreateQueryResult = CreateBaseQueryResult; ``` -Defined in: [types.ts:99](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L99) +Defined in: [types.ts:162](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L162) + +The result of `injectQuery`. Same as [CreateBaseQueryResult](CreateBaseQueryResult.md). ## Type Parameters @@ -15,6 +17,10 @@ Defined in: [types.ts:99](https://github.com/TanStack/query/blob/main/packages/a `TData` = `unknown` +The type `data` ends up as after `select` runs. + ### TError `TError` = `DefaultError` + +The type of errors your `queryFn` may throw. diff --git a/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md b/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md index c06fc7f7c08..4b56723f9ab 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md @@ -7,7 +7,11 @@ title: DefinedCreateInfiniteQueryResult type DefinedCreateInfiniteQueryResult = MapToSignals; ``` -Defined in: [types.ts:117](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L117) +Defined in: [types.ts:205](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L205) + +The result of `injectInfiniteQuery` when `initialData` is set — `data` is never `undefined`. Same shape as +DefinedInfiniteQueryObserverResult from `@tanstack/query-core`, but value fields are exposed as a +`Signal` while function fields are called directly, unchanged. ## Type Parameters @@ -15,10 +19,14 @@ Defined in: [types.ts:117](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type `data` ends up as after `select` runs. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TDefinedInfiniteQueryObserver `TDefinedInfiniteQueryObserver` = `DefinedInfiniteQueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md b/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md index 100c503cf4f..36e9b1de9de 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md @@ -7,7 +7,11 @@ title: DefinedCreateQueryResult type DefinedCreateQueryResult = BaseQueryNarrowing & MapToSignals>; ``` -Defined in: [types.ts:104](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L104) +Defined in: [types.ts:175](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L175) + +The result of `injectQuery` when `initialData` is set — `data` is never `undefined`. Same shape as +DefinedQueryObserverResult from `@tanstack/query-core`, but value fields are exposed as a +`Signal` while function fields are called directly, unchanged. ## Type Parameters @@ -15,10 +19,14 @@ Defined in: [types.ts:104](https://github.com/TanStack/query/blob/main/packages/ `TData` = `unknown` +The type `data` ends up as after `select` runs. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TState `TState` = `DefinedQueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md index 82d94c98551..8782c6118ec 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md @@ -7,7 +7,10 @@ title: DefinedInitialDataInfiniteOptions type DefinedInitialDataInfiniteOptions = CreateInfiniteQueryOptions & object; ``` -Defined in: [infinite-query-options.ts:62](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L62) +Defined in: [infinite-query-options.ts:109](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L109) + +The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set — `data` is +never `undefined`. ## Type Declaration @@ -20,24 +23,41 @@ initialData: | undefined; ``` +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. + ## Type Parameters ### TQueryFnData `TQueryFnData` +The type of a single page, as your `queryFn` resolves it. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `InfiniteData`\<`TQueryFnData`\> +The type `data` ends up as after `select` runs — defaults to `InfiniteData`, +the shape of all fetched pages plus their page params. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` +The type of your `queryKey`. + ### TPageParam `TPageParam` = `unknown` + +The type of the parameter passed to `queryFn` to fetch a given page. diff --git a/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md b/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md index 67df366a9cb..545b7346ef6 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md +++ b/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md @@ -7,7 +7,10 @@ title: DefinedInitialDataOptions type DefinedInitialDataOptions = Omit, "queryFn"> & object; ``` -Defined in: [query-options.ts:40](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L40) +Defined in: [query-options.ts:80](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L80) + +The options accepted by the `queryOptions` overload selected when `initialData` is set — `data` is never +`undefined`. ## Type Declaration @@ -19,26 +22,44 @@ initialData: | () => NonUndefinedGuard; ``` +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. + ### queryFn? ```ts optional queryFn: QueryFunction; ``` +Optional here, but omitting it is only safe when no fetch will be attempted — for example with +`enabled: false`, or when a default query function has been defined. Otherwise, an enabled query with no +`queryFn` still tries to fetch and fails with a "Missing queryFn" error; `initialData` does not prevent this. + ## Type Parameters ### TQueryFnData `TQueryFnData` = `unknown` +The type your `queryFn` resolves to. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `TQueryFnData` +The type `data` ends up as after `select` runs. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` + +The type of your `queryKey`. diff --git a/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md b/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md index 46eba9c321a..2ec2614ef8b 100644 --- a/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md +++ b/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md @@ -7,7 +7,7 @@ title: DevtoolsFeature type DevtoolsFeature = QueryFeature<"Devtools">; ``` -Defined in: [providers.ts:158](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L158) +Defined in: [providers.ts:155](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L155) A type alias that represents a feature which enables developer tools. The type is used to describe the return value of the `withDevtools` function. diff --git a/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md b/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md index 5522613defe..cb5f2440ccc 100644 --- a/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md +++ b/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md @@ -7,7 +7,7 @@ title: PersistQueryClientFeature type PersistQueryClientFeature = QueryFeature<"PersistQueryClient">; ``` -Defined in: [providers.ts:164](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L164) +Defined in: [providers.ts:161](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L161) A type alias that represents a feature which enables persistence. The type is used to describe the return value of the `withPersistQueryClient` function. diff --git a/docs/framework/angular/reference/type-aliases/QueriesOptions.md b/docs/framework/angular/reference/type-aliases/QueriesOptions.md index 3878020a629..850375e1e44 100644 --- a/docs/framework/angular/reference/type-aliases/QueriesOptions.md +++ b/docs/framework/angular/reference/type-aliases/QueriesOptions.md @@ -7,9 +7,12 @@ title: QueriesOptions type QueriesOptions = TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryOptionsForCreateQueries] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions<[...Tails], [...TResults, GetCreateQueryOptionsForCreateQueries], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; ``` -Defined in: [inject-queries.ts:144](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L144) +Defined in: [inject-queries.ts:153](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L153) -QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param +The `queries` array accepted by `injectQueries`. Recursively unwraps each tuple element so every entry's +`queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements. An opaque array (e.g. +`unknown[]`) is returned as-is; a non-tuple array of a known element type, or a tuple past 20 elements, +falls back to a single homogeneous options type. ## Type Parameters @@ -17,10 +20,18 @@ QueriesOptions reducer recursively unwraps function arguments to infer/enforce t `T` *extends* `any`[] +The type of the `queries` array as written at the call site. + ### TResults `TResults` *extends* `any`[] = \[\] +The internal accumulator that this type builds during recursion. It is not meant to +be set explicitly. + ### TDepth `TDepth` *extends* `ReadonlyArray`\<`number`\> = \[\] + +The internal recursion-depth counter, checked against the 20-element limit. It is not +meant to be set explicitly. diff --git a/docs/framework/angular/reference/type-aliases/QueriesResults.md b/docs/framework/angular/reference/type-aliases/QueriesResults.md index 20de4584240..ba74e04daf7 100644 --- a/docs/framework/angular/reference/type-aliases/QueriesResults.md +++ b/docs/framework/angular/reference/type-aliases/QueriesResults.md @@ -7,9 +7,12 @@ title: QueriesResults type QueriesResults = TDepth["length"] extends MAXIMUM_DEPTH ? CreateQueryResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResult] : T extends [infer Head, ...(infer Tails)] ? QueriesResults<[...Tails], [...TResults, GetCreateQueryResult], [...TDepth, 1]> : { [K in keyof T]: GetCreateQueryResult }; ``` -Defined in: [inject-queries.ts:186](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L186) +Defined in: [inject-queries.ts:204](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L204) -QueriesResults reducer recursively maps type param to results +The result type returned by `injectQueries`, when no `combine` is provided. Mirrors [QueriesOptions](QueriesOptions.md): +each tuple element's result type is inferred individually, up to 20 elements. A non-tuple array is mapped +per-element instead, still inferring each entry individually; only past 20 elements does this fall back to +a single homogeneous [CreateQueryResult](CreateQueryResult.md) type. ## Type Parameters @@ -17,10 +20,18 @@ QueriesResults reducer recursively maps type param to results `T` *extends* `any`[] +The type of the `queries` array, as inferred by [QueriesOptions](QueriesOptions.md). + ### TResults `TResults` *extends* `any`[] = \[\] +The internal accumulator that this type builds during recursion. It is not meant to +be set explicitly. + ### TDepth `TDepth` *extends* `ReadonlyArray`\<`number`\> = \[\] + +The internal recursion-depth counter, checked against the 20-element limit. It is not +meant to be set explicitly. diff --git a/docs/framework/angular/reference/type-aliases/QueryFeatures.md b/docs/framework/angular/reference/type-aliases/QueryFeatures.md index 4b9e2ac2930..ca829d2faf2 100644 --- a/docs/framework/angular/reference/type-aliases/QueryFeatures.md +++ b/docs/framework/angular/reference/type-aliases/QueryFeatures.md @@ -9,7 +9,7 @@ type QueryFeatures = | PersistQueryClientFeature; ``` -Defined in: [providers.ts:173](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L173) +Defined in: [providers.ts:170](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L170) A type alias that represents all Query features available for use with `provideTanStackQuery`. Features can be enabled by adding special functions to the `provideTanStackQuery` call. diff --git a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md index 726e00e139d..ea99866a0f0 100644 --- a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md @@ -7,7 +7,10 @@ title: UndefinedInitialDataInfiniteOptions type UndefinedInitialDataInfiniteOptions = CreateInfiniteQueryOptions & object; ``` -Defined in: [infinite-query-options.ts:13](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L13) +Defined in: [infinite-query-options.ts:24](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L24) + +The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set — `data` +may be `undefined` while the query is `pending`. ## Type Declaration @@ -19,24 +22,41 @@ optional initialData: | InitialDataFunction>>; ``` +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. + ## Type Parameters ### TQueryFnData `TQueryFnData` +The type of a single page, as your `queryFn` resolves it. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `InfiniteData`\<`TQueryFnData`\> +The type `data` ends up as after `select` runs — defaults to `InfiniteData`, +the shape of all fetched pages plus their page params. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` +The type of your `queryKey`. + ### TPageParam `TPageParam` = `unknown` + +The type of the parameter passed to `queryFn` to fetch a given page. diff --git a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md index c45cf86a027..28e2b4d2fbb 100644 --- a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md +++ b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md @@ -7,7 +7,10 @@ title: UndefinedInitialDataOptions type UndefinedInitialDataOptions = CreateQueryOptions & object; ``` -Defined in: [query-options.ts:13](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L13) +Defined in: [query-options.ts:22](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L22) + +The options accepted by the `queryOptions` overload selected when no `initialData` is set — `data` may be +`undefined` while the query is `pending`. ## Type Declaration @@ -19,20 +22,34 @@ optional initialData: | NonUndefinedGuard; ``` +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. + ## Type Parameters ### TQueryFnData `TQueryFnData` = `unknown` +The type your `queryFn` resolves to. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `TQueryFnData` +The type `data` ends up as after `select` runs. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` + +The type of your `queryKey`. diff --git a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md index 0f334d0c781..499287c4f00 100644 --- a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md @@ -7,7 +7,11 @@ title: UnusedSkipTokenInfiniteOptions type UnusedSkipTokenInfiniteOptions = OmitKeyof, "queryFn"> & object; ``` -Defined in: [infinite-query-options.ts:34](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L34) +Defined in: [infinite-query-options.ts:64](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L64) + +The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set and +`queryFn` is not `skipToken` — same as [UndefinedInitialDataInfiniteOptions](UndefinedInitialDataInfiniteOptions.md), but `queryFn` may not be +`skipToken`. ## Type Declaration @@ -17,24 +21,40 @@ Defined in: [infinite-query-options.ts:34](https://github.com/TanStack/query/blo optional queryFn: Exclude["queryFn"], SkipToken | undefined>; ``` +`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. + ## Type Parameters ### TQueryFnData `TQueryFnData` +The type of a single page, as your `queryFn` resolves it. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `InfiniteData`\<`TQueryFnData`\> +The type `data` ends up as after `select` runs — defaults to `InfiniteData`, +the shape of all fetched pages plus their page params. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` +The type of your `queryKey`. + ### TPageParam `TPageParam` = `unknown` + +The type of the parameter passed to `queryFn` to fetch a given page. diff --git a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md index e50911c58fb..dc85f4f9732 100644 --- a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md +++ b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md @@ -7,7 +7,10 @@ title: UnusedSkipTokenOptions type UnusedSkipTokenOptions = OmitKeyof, "queryFn"> & object; ``` -Defined in: [query-options.ts:25](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L25) +Defined in: [query-options.ts:50](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L50) + +The options accepted by the `queryOptions` overload selected when no `initialData` is set and `queryFn` is +not `skipToken` — same as [UndefinedInitialDataOptions](UndefinedInitialDataOptions.md), but `queryFn` may not be `skipToken`. ## Type Declaration @@ -17,20 +20,33 @@ Defined in: [query-options.ts:25](https://github.com/TanStack/query/blob/main/pa optional queryFn: Exclude["queryFn"], SkipToken | undefined>; ``` +`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. + ## Type Parameters ### TQueryFnData `TQueryFnData` = `unknown` +The type your `queryFn` resolves to. + ### TError `TError` = `DefaultError` +The type of errors your `queryFn` may throw. + ### TData `TData` = `TQueryFnData` +The type `data` ends up as after `select` runs. + ### TQueryKey `TQueryKey` *extends* `QueryKey` = `QueryKey` + +The type of your `queryKey`. diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index b09b194c6b9..696f3a715b7 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -10,6 +10,17 @@ import type { } 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`, + * 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, @@ -23,6 +34,13 @@ export type UndefinedInitialDataInfiniteOptions< 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> @@ -31,6 +49,18 @@ export type UndefinedInitialDataInfiniteOptions< > } +/** + * 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`, + * 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, @@ -47,6 +77,12 @@ export type UnusedSkipTokenInfiniteOptions< >, '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, @@ -59,6 +95,17 @@ export type UnusedSkipTokenInfiniteOptions< > } +/** + * The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set — `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. + * @template TData - The type `data` ends up as after `select` runs — defaults to `InfiniteData`, + * 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, @@ -72,6 +119,13 @@ export type DefinedInitialDataInfiniteOptions< 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> | (() => NonUndefinedGuard>) @@ -79,11 +133,50 @@ export type DefinedInitialDataInfiniteOptions< } /** - * Allows sharing and re-using infinite query options in a type-safe way. + * 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' * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. + * export const projectsOptions = infiniteQueryOptions({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => fetchProjects(pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextId, + * initialData: { pages: [], pageParams: [] }, + * }) + * + * @Component({ + * selector: 'projects', + * template: ` + * + *
    + * @for (page of projectsQuery.data().pages; track $index) { + * @for (project of page.projects; track project.id) { + *
  • {{ project.name }}
  • + * } + * } + *
+ * `, + * }) + * export class Projects { + * projectsQuery = injectInfiniteQuery(() => projectsOptions) + * } + * ``` */ export function infiniteQueryOptions< TQueryFnData, @@ -109,11 +202,55 @@ export function infiniteQueryOptions< QueryKeyWithDataTag, TError> /** - * Allows sharing and re-using infinite query options in a type-safe way. + * 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' * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. + * 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()) { + * Error: {{ commentsQuery.error()?.message }} + * } @else { + *
    + * @for (page of commentsQuery.data().pages; track $index) { + * @for (comment of page.comments; track comment.id) { + *
  • {{ comment.text }}
  • + * } + * } + *
+ * } + * `, + * }) + * export class Comments { + * postId = signal('1') + * 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, @@ -139,11 +276,55 @@ export function infiniteQueryOptions< QueryKeyWithDataTag, TError> /** - * Allows sharing and re-using infinite query options in a type-safe way. + * 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. * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. + * @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()) { + * Error: {{ commentsQuery.error()?.message }} + * } @else { + *
    + * @for (page of commentsQuery.data().pages; track $index) { + * @for (comment of page.comments; track comment.id) { + *
  • {{ comment.text }}
  • + * } + * } + *
+ * } + * `, + * }) + * export class Comments { + * postId = signal('1') + * 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, @@ -168,13 +349,6 @@ export function infiniteQueryOptions< > & QueryKeyWithDataTag, TError> -/** - * Allows sharing and re-using infinite query options in a type-safe way. - * - * The `queryKey` will be tagged with the type from `queryFn`. - * @param options - The infinite query options to tag with the type from `queryFn`. - * @returns The tagged infinite query options. - */ export function infiniteQueryOptions(options: unknown) { return options } diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts index ee6de032409..194e8a3e249 100644 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ b/packages/angular-query-experimental/src/inject-infinite-query.ts @@ -32,11 +32,53 @@ export interface InjectInfiniteQueryOptions { } /** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. + * The options for `injectInfiniteQuery` are identical to `injectQuery`, with the addition of + * `initialPageParam`, `getNextPageParam`, `getPreviousPageParam`, and `maxPages`. Infinite queries can + * additively "load more" data onto an existing set of data, or "infinite scroll". + * + * This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, + * so the resulting `data` signal is never `undefined`. + * + * @remarks Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default + * refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user + * actions, or add conditions like `hasNextPage() && !isFetching()`. + * @see {@link infiniteQueryOptions} to share these options between `injectInfiniteQuery` and imperative APIs + * like `queryClient.fetchInfiniteQuery`. + * @param injectInfiniteQueryFn - A function returning the {@link DefinedInitialDataInfiniteOptions} to use — + * everything you can pass to `injectInfiniteQuery`, with `initialData` set. Similar to `computed` from + * Angular, this function runs in the reactive context, so signals read inside it drive the query. * @param options - Additional configuration. - * @returns The infinite query result. + * @returns The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, + * `hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and + * `data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default + * `InfiniteData` shape. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'projects', + * template: ` + * + *
    + * @for (page of projectsQuery.data().pages; track $index) { + * @for (project of page.projects; track project.id) { + *
  • {{ project.name }}
  • + * } + * } + *
+ * `, + * }) + * export class Projects { + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => fetchProjects(pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextId, + * initialData: { pages: [], pageParams: [] }, + * })) + * } + * ``` */ export function injectInfiniteQuery< TQueryFnData, @@ -56,11 +98,143 @@ export function injectInfiniteQuery< ): DefinedCreateInfiniteQueryResult /** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a + * unique key. Infinite queries can additively "load more" data onto an existing set of data, or + * "infinite scroll". + * + * @remarks Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default + * refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user + * actions, or add conditions like `hasNextPage() && !isFetching()`. This is the only overload that accepts + * `queryFn: skipToken`, shown below. + * @see {@link infiniteQueryOptions} to share these options between `injectInfiniteQuery` and imperative APIs + * like `queryClient.fetchInfiniteQuery`. + * @param injectInfiniteQueryFn - A function returning the {@link UndefinedInitialDataInfiniteOptions} to use + * — everything you can pass to `injectInfiniteQuery`. Similar to `computed` from Angular, this function runs + * in the reactive context, so signals read inside it drive the query. * @param options - Additional configuration. - * @returns The infinite query result. + * @returns The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, + * `hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and + * `data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default + * `InfiniteData` shape. + * + * @example + * Fetching the next page from a button click: + * ```angular-ts + * @Component({ + * selector: 'projects-list', + * template: ` + *
    + * @for (page of projectsQuery.data()?.pages; track $index) { + * @for (project of page.projects; track project.id) { + *
  • {{ project.name }}
  • + * } + * } + *
+ * + * `, + * }) + * export class ProjectsList { + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => fetchProjects(pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextId, + * })) + * } + * ``` + * + * @example + * Fetching the next page automatically as the user scrolls, using an `IntersectionObserver` on a sentinel + * element after the list: + * ```angular-ts + * @Component({ + * selector: 'projects-list', + * template: ` + *
    + * @for (page of projectsQuery.data()?.pages; track $index) { + * @for (project of page.projects; track project.id) { + *
  • {{ project.name }}
  • + * } + * } + *
+ *
{{ projectsQuery.isFetchingNextPage() ? 'Loading more...' : '' }}
+ * `, + * }) + * export class ProjectsList { + * sentinel = viewChild>('sentinel') + * + * projectsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => fetchProjects(pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextId, + * })) + * + * constructor() { + * effect((onCleanup) => { + * const sentinel = this.sentinel()?.nativeElement + * if ( + * sentinel == null || + * !this.projectsQuery.hasNextPage() || + * this.projectsQuery.isFetching() + * ) { + * return + * } + * + * const observer = new IntersectionObserver(([entry]) => { + * if (entry?.isIntersecting) this.projectsQuery.fetchNextPage() + * }) + * observer.observe(sentinel) + * + * onCleanup(() => observer.disconnect()) + * }) + * } + * } + * ``` + * + * @example + * A query that's disabled, type safe, until `postId` is set — pass `skipToken` as `queryFn` instead of + * setting `enabled: false`: + * ```angular-ts + * @Component({ + * selector: 'comments', + * template: ` + * @if (postId() == null) { + * Select a post + * } @else if (commentsQuery.isPending()) { + * Loading... + * } @else if (commentsQuery.isError()) { + * Error: {{ commentsQuery.error()?.message }} + * } @else { + *
    + * @for (page of commentsQuery.data().pages; track $index) { + * @for (comment of page.comments; track comment.id) { + *
  • {{ comment.text }}
  • + * } + * } + *
+ * } + * `, + * }) + * export class Comments { + * postId = signal(undefined) + * + * commentsQuery = injectInfiniteQuery(() => ({ + * queryKey: ['post', this.postId(), 'comments'], + * queryFn: + * this.postId() != null + * ? ({ pageParam }) => fetchComments(this.postId()!, pageParam) + * : skipToken, + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextId, + * })) + * } + * ``` */ export function injectInfiniteQuery< TQueryFnData, @@ -80,9 +254,13 @@ export function injectInfiniteQuery< ): CreateInfiniteQueryResult /** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. + * This overload accepts the general {@link CreateInfiniteQueryOptions} shape rather than the + * `initialData`-aware overloads above, so whether `data` is defined can't be inferred from the call site — + * useful when wrapping `injectInfiniteQuery` in your own helper function that forwards caller-provided + * options. + * + * @param injectInfiniteQueryFn - A function that returns infinite query options. Similar to `computed` from + * Angular, this function runs in the reactive context, so signals read inside it drive the query. * @param options - Additional configuration. * @returns The infinite query result. */ @@ -103,13 +281,6 @@ export function injectInfiniteQuery< options?: InjectInfiniteQueryOptions, ): CreateInfiniteQueryResult -/** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" - * @param injectInfiniteQueryFn - A function that returns infinite query options. - * @param options - Additional configuration. - * @returns The infinite query result. - */ export function injectInfiniteQuery( injectInfiniteQueryFn: () => CreateInfiniteQueryOptions, options?: InjectInfiniteQueryOptions, diff --git a/packages/angular-query-experimental/src/inject-is-fetching.ts b/packages/angular-query-experimental/src/inject-is-fetching.ts index 4bb3e821dbb..6007a089c44 100644 --- a/packages/angular-query-experimental/src/inject-is-fetching.ts +++ b/packages/angular-query-experimental/src/inject-is-fetching.ts @@ -20,13 +20,45 @@ export interface InjectIsFetchingOptions { } /** - * Injects a signal that tracks the number of queries that your application is loading or - * fetching in the background. + * Injects a signal that tracks the number of queries that your application is loading or fetching in the + * background (useful for app-wide loading indicators). * - * Can be used for app-wide loading indicators - * @param filters - The filters to apply to the query. + * @param filters - The {@link QueryFilters} to narrow down the matched queries. * @param options - Additional configuration - * @returns signal with number of loading or fetching queries. + * @returns A `Signal` with the number of queries that your application is currently loading or fetching in + * the background. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'posts-fetching-indicator', + * template: ` + * @if (isFetchingPosts()) { + * Refreshing posts... + * } + * `, + * }) + * export class PostsFetchingIndicator { + * // How many queries matching the posts prefix are fetching? + * isFetchingPosts = injectIsFetching({ queryKey: ['posts'] }) + * } + * ``` + * + * @example + * A global loading indicator for any query fetching in the background, not just the ones on screen: + * ```angular-ts + * @Component({ + * selector: 'global-loading-indicator', + * template: ` + * @if (isFetching()) { + *
Queries are fetching in the background...
+ * } + * `, + * }) + * export class GlobalLoadingIndicator { + * isFetching = injectIsFetching() + * } + * ``` */ export function injectIsFetching( filters?: QueryFilters, diff --git a/packages/angular-query-experimental/src/inject-is-mutating.ts b/packages/angular-query-experimental/src/inject-is-mutating.ts index 7bab410634d..f1ce3f821f4 100644 --- a/packages/angular-query-experimental/src/inject-is-mutating.ts +++ b/packages/angular-query-experimental/src/inject-is-mutating.ts @@ -20,12 +20,28 @@ export interface InjectIsMutatingOptions { } /** - * Injects a signal that tracks the number of mutations that your application is fetching. + * Injects a signal that tracks the number of mutations that your application currently has `pending` + * (useful for app-wide loading indicators). * - * Can be used for app-wide loading indicators - * @param filters - The filters to apply to the query. + * @param filters - The {@link MutationFilters} to narrow down the matched mutations. * @param options - Additional configuration - * @returns A read-only signal with the number of fetching mutations. + * @returns A `Signal` with the number of mutations that your application currently has `pending`. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'posts-mutating-indicator', + * template: ` + * @if (isMutatingPosts()) { + * Saving posts... + * } + * `, + * }) + * export class PostsMutatingIndicator { + * // How many mutations matching the posts prefix are in progress? + * isMutatingPosts = injectIsMutating({ mutationKey: ['posts'] }) + * } + * ``` */ export function injectIsMutating( filters?: MutationFilters, diff --git a/packages/angular-query-experimental/src/inject-is-restoring.ts b/packages/angular-query-experimental/src/inject-is-restoring.ts index 2c7fb3ae687..f73310affc5 100644 --- a/packages/angular-query-experimental/src/inject-is-restoring.ts +++ b/packages/angular-query-experimental/src/inject-is-restoring.ts @@ -25,9 +25,12 @@ interface InjectIsRestoringOptions { } /** - * Injects a signal that tracks whether a restore is currently in progress. {@link injectQuery} and friends also check this internally to avoid race conditions between the restore and initializing queries. - * @param options - Options for injectIsRestoring. - * @returns readonly signal with boolean that indicates whether a restore is in progress. + * Injects a signal that tracks whether a restore (e.g. from a persisted client, wired up via + * `provideIsRestoring`) is currently in progress. `injectQuery` and friends also check this internally to + * avoid race conditions between the restore and initializing queries. + * @param options - Additional configuration + * @returns A readonly `Signal` — `true` while a restore is in progress, `false` otherwise (the + * default when no `provideIsRestoring` provider is registered). */ export function injectIsRestoring(options?: InjectIsRestoringOptions) { !options?.injector && assertInInjectionContext(injectIsRestoring) @@ -36,9 +39,11 @@ export function injectIsRestoring(options?: InjectIsRestoringOptions) { } /** - * Used by TanStack Query Angular persist client plugin to provide the signal that tracks the restore state - * @param isRestoring - a readonly signal that returns a boolean - * @returns Provider for the `isRestoring` signal + * Registers a provider for the restore state read by `injectIsRestoring`. Wire this up wherever you drive a + * restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can hold off + * initializing queries until the restore signal flips back to `false`. + * @param isRestoring - A readonly `Signal` that tracks the restore state. + * @returns A provider for the `isRestoring` signal. */ export function provideIsRestoring(isRestoring: Signal): Provider { return { diff --git a/packages/angular-query-experimental/src/inject-mutation-state.ts b/packages/angular-query-experimental/src/inject-mutation-state.ts index 5f8996b2b18..a9f136a5174 100644 --- a/packages/angular-query-experimental/src/inject-mutation-state.ts +++ b/packages/angular-query-experimental/src/inject-mutation-state.ts @@ -25,11 +25,6 @@ type MutationStateOptions = { select?: (mutation: Mutation) => TResult } -/** - * - * @param mutationCache - * @param options - */ function getResult( mutationCache: MutationCache, options: MutationStateOptions, @@ -52,10 +47,61 @@ export interface InjectMutationStateOptions { } /** - * Injects a signal that tracks the state of all mutations. - * @param injectMutationStateFn - A function that returns mutation state options. - * @param options - The Angular injector to use. - * @returns The signal that tracks the state of all mutations. + * Injects a signal that gives you access to all mutations in the `MutationCache`. You can pass `filters` + * ({@link MutationFilters}) to narrow down your mutations, and `select` to transform the mutation state. + * + * @param injectMutationStateFn - A function returning the `filters` to narrow down matched mutations, and an + * optional `select` to transform the mutation state. Similar to `computed` from Angular, this function runs + * in the reactive context, so signals read inside it re-narrow the matched mutations. + * @param options - Additional configuration + * @returns A `Signal` with an Array of whatever `select` returns for each matching mutation. + * + * @example + * Get all variables of all running mutations: + * ```angular-ts + * @Component({ + * selector: 'pending-posts', + * template: `{{ pendingVariables().length }} posts saving...`, + * }) + * export class PendingPosts { + * pendingVariables = injectMutationState(() => ({ + * filters: { status: 'pending' }, + * select: (mutation) => mutation.state.variables, + * })) + * } + * ``` + * + * @example + * Get all data for specific mutations via the `mutationKey`: + * ```angular-ts + * const mutationKey = ['posts'] + * + * @Component({ + * selector: 'posts', + * template: ` + * + * `, + * }) + * export class Posts { + * // Some mutation that we want to get the state for + * createPostMutation = injectMutation(() => ({ + * mutationKey, + * mutationFn: createPosts, + * })) + * + * savedPosts = injectMutationState(() => ({ + * // this mutation key needs to match the mutation key of the given mutation (see above) + * filters: { mutationKey, status: 'success' }, + * select: (mutation) => mutation.state.data, + * })) + * + * createPost() { + * this.createPostMutation.mutate(['New Post']) + * } + * } + * ``` */ export function injectMutationState( injectMutationStateFn: () => MutationStateOptions = () => ({}), diff --git a/packages/angular-query-experimental/src/inject-mutation.ts b/packages/angular-query-experimental/src/inject-mutation.ts index 8acad495329..18c919b78b4 100644 --- a/packages/angular-query-experimental/src/inject-mutation.ts +++ b/packages/angular-query-experimental/src/inject-mutation.ts @@ -35,12 +35,141 @@ export interface InjectMutationOptions { } /** - * Injects a mutation: an imperative function that can be invoked which typically performs server side effects. + * Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. + * `injectMutation` is the function for that. Unlike queries, mutations are not run automatically. * - * Unlike queries, mutations are not run automatically. - * @param injectMutationFn - A function that returns mutation options. + * @remarks `mutate`/`mutateAsync` also accept per-call `onSuccess`/`onError`/`onSettled` callbacks as a + * second argument, useful for triggering call-site side effects (e.g. navigation) without coupling them to + * the shared mutation definition. Callbacks defined in `injectMutationFn` fire for every mutation; per-call + * callbacks fire only for the latest call you've made — `mutateAsync` gives you a promise per call instead, + * so you can await `Promise.all`/`Promise.allSettled` over several calls and see each one's outcome. + * @see {@link mutationOptions} to share these options across multiple `injectMutation` call sites, or to look + * the mutation up elsewhere via its `mutationKey` (e.g. with `injectMutationState`). + * @param injectMutationFn - A function that returns mutation options. Similar to `computed` from Angular, + * this function runs in the reactive context, so signals read inside it drive the mutation's options. * @param options - Additional configuration - * @returns The mutation. + * @returns The mutation result. Value fields are exposed as a `Signal` — read `data`/`error` by calling them + * (e.g. `mutation.data()`) — while function fields (`mutate`, `mutateAsync`, `reset`) are called directly, + * unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are type-guard methods you can call to narrow whether + * `data` is defined. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'todos', + * template: ` + * @if (addMutation.isPending()) { + * Adding todo... + * } @else if (addMutation.isError()) { + *
An error occurred: {{ addMutation.error()?.message }}
+ * } + * + * `, + * }) + * export class Todos { + * #queryClient = inject(QueryClient) + * + * addMutation = injectMutation(() => ({ + * mutationFn: addTodo, + * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), + * })) + * } + * ``` + * + * @example + * Optimistic update via `onMutate`, rolling back on `onError`: + * ```angular-ts + * @Component({ + * selector: 'todos', + * template: ``, + * }) + * export class Todos { + * #queryClient = inject(QueryClient) + * + * addMutation = injectMutation(() => ({ + * mutationFn: addTodo, + * onMutate: async (newTodo) => { + * await this.#queryClient.cancelQueries({ queryKey: ['todos'] }) + * const previousTodos = this.#queryClient.getQueryData>(['todos']) + * + * this.#queryClient.setQueryData>(['todos'], (old) => [ + * ...(old ?? []), + * newTodo, + * ]) + * + * // Passed to `onError` as `onMutateResult` if the mutation fails. + * return { previousTodos } + * }, + * onError: (_err, _newTodo, onMutateResult) => { + * this.#queryClient.setQueryData(['todos'], onMutateResult?.previousTodos) + * }, + * onSettled: () => { + * this.#queryClient.invalidateQueries({ queryKey: ['todos'] }) + * }, + * })) + * } + * ``` + * + * @example + * Callbacks passed per call to `mutate` only fire for the last call — `mutateAsync` gives you a promise per + * call instead, so you can wait for all of them: + * ```angular-ts + * @Component({ + * selector: 'todos', + * template: ` + * + * `, + * }) + * export class Todos { + * #queryClient = inject(QueryClient) + * + * addMutation = injectMutation(() => ({ + * mutationFn: addTodo, + * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), + * })) + * + * async handleAddAll(todos: Array) { + * try { + * await Promise.all(todos.map((todo) => this.addMutation.mutateAsync(todo))) + * } catch (error) { + * console.error('Failed to add todos:', error) + * } + * } + * } + * ``` + * + * @example + * If some of the mutations above can fail independently of the others, and you want to know which ones did — + * rather than losing that information the moment the first one rejects — swap `Promise.all` for + * `Promise.allSettled`: + * ```angular-ts + * @Component({ + * selector: 'todos', + * template: ` + * + * `, + * }) + * export class Todos { + * #queryClient = inject(QueryClient) + * + * addMutation = injectMutation(() => ({ + * mutationFn: addTodo, + * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), + * })) + * + * async handleAddAll(todos: Array) { + * const addResults = await Promise.allSettled( + * todos.map((todo) => this.addMutation.mutateAsync(todo)), + * ) + * + * addResults.forEach((addResult, index) => { + * if (addResult.status === 'rejected') { + * console.error(`Failed to add "${todos[index]}":`, addResult.reason) + * } + * }) + * } + * } + * ``` */ export function injectMutation< TData = unknown, diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts index d61a937a3c2..218c44ac8ba 100644 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ b/packages/angular-query-experimental/src/inject-queries.ts @@ -139,7 +139,16 @@ type GetCreateQueryResult = CreateQueryResult /** - * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + * The `queries` array accepted by `injectQueries`. Recursively unwraps each tuple element so every entry's + * `queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements. An opaque array (e.g. + * `unknown[]`) is returned as-is; a non-tuple array of a known element type, or a tuple past 20 elements, + * falls back to a single homogeneous options type. + * + * @template T - The type of the `queries` array as written at the call site. + * @template TResults - The internal accumulator that this type builds during recursion. It is not meant to + * be set explicitly. + * @template TDepth - The internal recursion-depth counter, checked against the 20-element limit. It is not + * meant to be set explicitly. */ export type QueriesOptions< T extends Array, @@ -181,7 +190,16 @@ export type QueriesOptions< Array /** - * QueriesResults reducer recursively maps type param to results + * The result type returned by `injectQueries`, when no `combine` is provided. Mirrors {@link QueriesOptions}: + * each tuple element's result type is inferred individually, up to 20 elements. A non-tuple array is mapped + * per-element instead, still inferring each entry individually; only past 20 elements does this fall back to + * a single homogeneous {@link CreateQueryResult} type. + * + * @template T - The type of the `queries` array, as inferred by {@link QueriesOptions}. + * @template TResults - The internal accumulator that this type builds during recursion. It is not meant to + * be set explicitly. + * @template TDepth - The internal recursion-depth counter, checked against the 20-element limit. It is not + * meant to be set explicitly. */ export type QueriesResults< T extends Array, @@ -214,8 +232,138 @@ export interface InjectQueriesOptions< } /** - * @param optionsFn - A function that returns queries' options. - * @param injector - The Angular injector to use. + * Injects a signal to fetch a variable number of queries. + * + * The `queries` key accepts an array with query option objects mostly identical to `injectQuery`'s. Having + * the same query key more than once in the array of query objects may cause some data to be shared between + * queries. To avoid this, consider de-duplicating the queries and map the results back to the desired + * structure. + * + * The `combine` option can be used to combine the results of the queries into a single value. The result + * will be structurally shared to be as referentially stable as possible. + * + * @remarks Unlike `injectQuery`, `injectQueries` cannot infer the `data` argument of an _inline_ `select` + * from its sibling `queryFn`. Because `injectQueries` infers the type of the whole `queries` array at once, + * the `select` parameter of a query object written inline cannot be contextually typed from that same + * object's `queryFn`, so it falls back to `unknown` — a + * [known TypeScript limitation](https://github.com/TanStack/query/issues/6556). Annotate the `select` + * parameter explicitly, or define the query with {@link queryOptions}, which resolves its types in a single + * object _before_ it reaches `injectQueries`, to work around this — see the example below. + * @param optionsFn - A function returning the queries' options — an array of query option objects under + * `queries`, and an optional `combine`. Similar to `computed` from Angular, this function runs in the + * reactive context, so signals read inside it (e.g. to build the `queries` array) drive the queries. + * @param injector - The `Injector` in which to create the queries. If this is not provided, the current + * injection context will be used instead (via `inject`). + * @returns A `Signal` with the combined result. Without `combine`, this is an array with all the query + * results, in the same order as the input. When `combine` is provided, this is the value returned by + * `combine` instead. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'posts', + * template: ` + *
    + * @for (query of postQueries(); track $index) { + * @if (query.isPending()) { + *
  • Loading...
  • + * } @else if (query.isError()) { + *
  • Error: {{ query.error()?.message }}
  • + * } @else { + *
  • {{ query.data().title }}
  • + * } + * } + *
+ * `, + * }) + * export class Posts { + * ids = signal([1, 2, 3]) + * + * postQueries = injectQueries(() => ({ + * queries: this.ids().map((id) => ({ + * queryKey: ['post', id], + * queryFn: () => fetchPost(id), + * staleTime: Infinity, + * })), + * })) + * } + * ``` + * + * @example + * Combining results into a single value: + * ```angular-ts + * @Component({ + * selector: 'posts', + * template: ` + * @if (combined().isPending) { + * Loading... + * } @else if (combined().isError) { + * Error loading posts + * } @else { + *
    + * @for (post of combined().data; track post?.id) { + *
  • {{ post?.title }}
  • + * } + *
+ * } + * `, + * }) + * export class Posts { + * ids = signal([1, 2, 3]) + * + * combined = injectQueries(() => ({ + * queries: this.ids().map((id) => ({ + * queryKey: ['post', id], + * queryFn: () => fetchPost(id), + * })), + * combine: (postQueries) => ({ + * data: postQueries.map((query) => query.data), + * isPending: postQueries.some((query) => query.isPending), + * isError: postQueries.some((query) => query.isError), + * }), + * })) + * } + * ``` + * + * @example + * Typing `select` via {@link queryOptions}. Note that spreading a `queryOptions` result and overriding + * `select` inline still falls back to `unknown` — wrap the spread in `queryOptions` again so the override is + * resolved before it reaches `injectQueries`: + * ```angular-ts + * const postOptions = (id: number) => + * queryOptions({ + * queryKey: ['post', id], + * queryFn: () => fetchPost(id), + * }) + * + * @Component({ + * selector: 'post-title', + * template: `

{{ fixed()[0].data() }}

`, + * }) + * export class PostTitle { + * id = signal(1) + * + * broken = injectQueries(() => ({ + * queries: [ + * { + * ...postOptions(this.id()), + * // ❌ `data` is `unknown` here + * select: (data) => data.title, + * }, + * ], + * })) + * + * fixed = injectQueries(() => ({ + * queries: [ + * queryOptions({ + * ...postOptions(this.id()), + * // ✅ `data` is `Post` + * select: (data) => data.title, + * }), + * ], + * })) + * } + * ``` */ export function injectQueries< T extends Array, diff --git a/packages/angular-query-experimental/src/inject-query.ts b/packages/angular-query-experimental/src/inject-query.ts index 1dac0ab6949..4a6beb3e953 100644 --- a/packages/angular-query-experimental/src/inject-query.ts +++ b/packages/angular-query-experimental/src/inject-query.ts @@ -27,40 +27,43 @@ export interface InjectQueryOptions { } /** - * Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * - * **Basic example** - * ```ts - * class ServiceOrComponent { - * query = injectQuery(() => ({ - * queryKey: ['repoData'], - * queryFn: () => - * this.#http.get('https://api.github.com/repos/tanstack/query'), - * })) - * } - * ``` - * - * Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. - * In the example below, the query will be automatically enabled and executed when the filter signal changes - * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - * - * **Reactive example** - * ```ts - * class ServiceOrComponent { - * filter = signal('') + * This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the + * resulting `data` signal is never `undefined`. * - * todosQuery = injectQuery(() => ({ - * queryKey: ['todos', this.filter()], - * queryFn: () => fetchTodos(this.filter()), - * // Signals can be combined with expressions - * enabled: !!this.filter(), + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries + * @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like + * `queryClient.fetchQuery`. + * @param injectQueryFn - A function returning the {@link DefinedInitialDataOptions} to use — everything you + * can pass to `injectQuery`, with `initialData` set. Similar to `computed` from Angular, this function runs + * in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. + * @param options - Additional configuration + * @returns The query result, typed so that `data` is never `undefined`. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'posts', + * template: ` + * + * @if (postsQuery.isError()) { + * Error: {{ postsQuery.error()?.message }} + * } + *
    + * @for (post of postsQuery.data(); track post.id) { + *
  • {{ post.title }}
  • + * } + *
+ * `, + * }) + * export class Posts { + * postsQuery = injectQuery(() => ({ + * queryKey: ['posts'], + * queryFn: fetchPosts, + * initialData: [], * })) * } * ``` - * @param injectQueryFn - A function that returns query options. - * @param options - Additional configuration - * @returns The query result. - * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries */ export function injectQuery< TQueryFnData = unknown, @@ -80,38 +83,76 @@ export function injectQuery< /** * Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. * - * **Basic example** - * ```ts - * class ServiceOrComponent { - * query = injectQuery(() => ({ - * queryKey: ['repoData'], - * queryFn: () => - * this.#http.get('https://api.github.com/repos/tanstack/query'), + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries + * @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like + * `queryClient.fetchQuery`. + * @param injectQueryFn - A function returning the {@link UndefinedInitialDataOptions} to use — everything + * you can pass to `injectQuery`. Similar to `computed` from Angular, this function runs in the reactive + * context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. + * @param options - Additional configuration + * @returns The query result. `status()` is `'pending'` if there is no cached data to display, `'error'` if + * the last fetch attempt failed, or `'success'` if the query has data to display. `isPending`/`isSuccess`/ + * `isError` are type-guard methods for convenience. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'posts', + * template: ` + * @if (postsQuery.isPending()) { + * Loading... + * } @else if (postsQuery.isError()) { + * Error: {{ postsQuery.error()?.message }} + * } @else { + *
    + * @for (post of postsQuery.data(); track post.id) { + *
  • {{ post.title }}
  • + * } + *
+ * } + * `, + * }) + * export class Posts { + * postsQuery = injectQuery(() => ({ + * queryKey: ['posts'], + * queryFn: fetchPosts, * })) * } * ``` * - * Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. - * In the example below, the query will be automatically enabled and executed when the filter signal changes - * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - * - * **Reactive example** - * ```ts - * class ServiceOrComponent { + * @example + * Similar to `computed` from Angular, the function passed to `injectQuery` runs in the reactive context. In + * the example below, the query is automatically enabled and executed when the filter signal changes to a + * truthy value. When the filter signal changes back to a falsy value, the query is disabled. + * ```angular-ts + * @Component({ + * selector: 'posts', + * template: ` + * + * @if (postsQuery.isPending()) { + * Loading... + * } @else if (postsQuery.isError()) { + * Error: {{ postsQuery.error()?.message }} + * } @else { + *
    + * @for (post of postsQuery.data(); track post.id) { + *
  • {{ post.title }}
  • + * } + *
+ * } + * `, + * }) + * export class Posts { * filter = signal('') * - * todosQuery = injectQuery(() => ({ - * queryKey: ['todos', this.filter()], - * queryFn: () => fetchTodos(this.filter()), + * postsQuery = injectQuery(() => ({ + * queryKey: ['posts', this.filter()], + * queryFn: () => fetchPosts(this.filter()), * // Signals can be combined with expressions * enabled: !!this.filter(), * })) * } * ``` - * @param injectQueryFn - A function that returns query options. - * @param options - Additional configuration - * @returns The query result. - * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries */ export function injectQuery< TQueryFnData = unknown, @@ -129,40 +170,16 @@ export function injectQuery< ): CreateQueryResult /** - * Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * This overload accepts the general {@link CreateQueryOptions} shape rather than the `initialData`-aware + * overloads above, so whether `data` is defined can't be inferred from the call site — useful when wrapping + * `injectQuery` in your own helper function that forwards caller-provided options. * - * **Basic example** - * ```ts - * class ServiceOrComponent { - * query = injectQuery(() => ({ - * queryKey: ['repoData'], - * queryFn: () => - * this.#http.get('https://api.github.com/repos/tanstack/query'), - * })) - * } - * ``` - * - * Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. - * In the example below, the query will be automatically enabled and executed when the filter signal changes - * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - * - * **Reactive example** - * ```ts - * class ServiceOrComponent { - * filter = signal('') - * - * todosQuery = injectQuery(() => ({ - * queryKey: ['todos', this.filter()], - * queryFn: () => fetchTodos(this.filter()), - * // Signals can be combined with expressions - * enabled: !!this.filter(), - * })) - * } - * ``` - * @param injectQueryFn - A function that returns query options. + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries + * @param injectQueryFn - A function that returns query options. Similar to `computed` from Angular, this + * function runs in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive + * the query. * @param options - Additional configuration * @returns The query result. - * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries */ export function injectQuery< TQueryFnData = unknown, @@ -179,42 +196,6 @@ export function injectQuery< options?: InjectQueryOptions, ): CreateQueryResult -/** - * Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. - * - * **Basic example** - * ```ts - * class ServiceOrComponent { - * query = injectQuery(() => ({ - * queryKey: ['repoData'], - * queryFn: () => - * this.#http.get('https://api.github.com/repos/tanstack/query'), - * })) - * } - * ``` - * - * Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. - * In the example below, the query will be automatically enabled and executed when the filter signal changes - * to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. - * - * **Reactive example** - * ```ts - * class ServiceOrComponent { - * filter = signal('') - * - * todosQuery = injectQuery(() => ({ - * queryKey: ['todos', this.filter()], - * queryFn: () => fetchTodos(this.filter()), - * // Signals can be combined with expressions - * enabled: !!this.filter(), - * })) - * } - * ``` - * @param injectQueryFn - A function that returns query options. - * @param options - Additional configuration - * @returns The query result. - * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries - */ export function injectQuery( injectQueryFn: () => CreateQueryOptions, options?: InjectQueryOptions, diff --git a/packages/angular-query-experimental/src/mutation-options.ts b/packages/angular-query-experimental/src/mutation-options.ts index de59b994296..775cd00c117 100644 --- a/packages/angular-query-experimental/src/mutation-options.ts +++ b/packages/angular-query-experimental/src/mutation-options.ts @@ -2,39 +2,40 @@ import type { DefaultError, WithRequired } from '@tanstack/query-core' import type { CreateMutationOptions } from './types' /** - * Allows sharing and re-using mutation options in a type-safe way. + * You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. A + * `mutationKey` is required on this overload so the mutation can be looked up later, e.g. with + * `injectMutationState`. * - * **Example** + * @see {@link injectMutation} to run the mutation these options describe. + * @param options - The mutation options to use, identical to what you'd pass to `injectMutation`, with a + * required `mutationKey`. + * @returns The same options object, unchanged. * - * ```ts - * export class QueriesService { - * private http = inject(HttpClient) - * private queryClient = inject(QueryClient) - * - * updatePost(id: number) { - * return mutationOptions({ - * mutationFn: (post: Post) => Promise.resolve(post), - * mutationKey: ["updatePost", id], - * onSuccess: (newPost) => { - * // ^? newPost: Post - * this.queryClient.setQueryData(["posts", id], newPost) - * }, - * }); - * } - * } + * @example + * Looking the mutation up elsewhere via its `mutationKey`, e.g. for a global "saving…" indicator: + * ```angular-ts + * import { mutationOptions, injectMutationState } from '@tanstack/angular-query-experimental' * - * class ComponentOrService { - * queries = inject(QueriesService) - * id = signal(0) - * mutation = injectMutation(() => this.queries.updatePost(this.id())) + * const createPostOptions = mutationOptions({ + * mutationKey: ['posts', 'create'], + * mutationFn: createPost, + * }) * - * save() { - * this.mutation.mutate({ title: 'New Title' }) - * } + * @Component({ + * selector: 'saving-indicator', + * template: ` + * @if (isCreatingPost()) { + * Saving… + * } + * `, + * }) + * export class SavingIndicator { + * #pendingCreates = injectMutationState(() => ({ + * filters: { mutationKey: createPostOptions.mutationKey, status: 'pending' }, + * })) + * isCreatingPost = computed(() => this.#pendingCreates().length > 0) * } * ``` - * @param options - The mutation options. - * @returns Mutation options. */ export function mutationOptions< TData = unknown, @@ -50,56 +51,64 @@ export function mutationOptions< CreateMutationOptions, 'mutationKey' > -export function mutationOptions< - TData = unknown, - TError = DefaultError, - TVariables = void, - TOnMutateResult = unknown, ->( - options: Omit< - CreateMutationOptions, - 'mutationKey' - >, -): Omit< - CreateMutationOptions, - 'mutationKey' -> - /** - * Allows sharing and re-using mutation options in a type-safe way. + * You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. No + * `mutationKey` is required on this overload — use this when you don't need to target the mutation via a + * `mutationKey` filter later (e.g. with `injectMutationState`); it can still be observed through other + * filters, such as `status`. * - * **Example** + * @see {@link injectMutation} to run the mutation these options describe. + * @param options - The mutation options to use, identical to what you'd pass to `injectMutation`, without a + * `mutationKey`. + * @returns The same options object, unchanged. + * @remarks See the other overload's example for looking a mutation up via `injectMutationState`. * - * ```ts + * @example + * Sharing options across services, so `QueriesService` stays the single place a mutation is defined: + * ```angular-ts + * import { mutationOptions, injectMutation } from '@tanstack/angular-query-experimental' + * + * @Injectable({ providedIn: 'root' }) * export class QueriesService { - * private http = inject(HttpClient) - * private queryClient = inject(QueryClient) + * #queryClient = inject(QueryClient) * * updatePost(id: number) { * return mutationOptions({ - * mutationFn: (post: Post) => Promise.resolve(post), - * mutationKey: ["updatePost", id], - * onSuccess: (newPost) => { - * // ^? newPost: Post - * this.queryClient.setQueryData(["posts", id], newPost) - * }, - * }); + * mutationFn: (post: Partial) => putPost(id, post), + * onSuccess: (newPost) => this.#queryClient.setQueryData(['posts', id], newPost), + * }) * } * } * - * class ComponentOrService { + * @Component({ + * selector: 'post', + * template: ``, + * }) + * export class Post { * queries = inject(QueriesService) * id = signal(0) - * mutation = injectMutation(() => this.queries.updatePost(this.id())) + * updatePostMutation = injectMutation(() => this.queries.updatePost(this.id())) * * save() { - * this.mutation.mutate({ title: 'New Title' }) + * this.updatePostMutation.mutate({ title: 'New Title' }) * } * } * ``` - * @param options - The mutation options. - * @returns Mutation options. */ +export function mutationOptions< + TData = unknown, + TError = DefaultError, + TVariables = void, + TOnMutateResult = unknown, +>( + options: Omit< + CreateMutationOptions, + 'mutationKey' + >, +): Omit< + CreateMutationOptions, + 'mutationKey' +> export function mutationOptions< TData = unknown, TError = DefaultError, diff --git a/packages/angular-query-experimental/src/providers.ts b/packages/angular-query-experimental/src/providers.ts index 7d17f93e984..9f52c11690f 100644 --- a/packages/angular-query-experimental/src/providers.ts +++ b/packages/angular-query-experimental/src/providers.ts @@ -4,12 +4,20 @@ import type { Provider } from '@angular/core' /** * Usually {@link provideTanStackQuery} is used once to set up TanStack Query and the - * {@link https://tanstack.com/query/latest/docs/reference/QueryClient|QueryClient} - * for the entire application. Internally it calls `provideQueryClient`. - * You can use `provideQueryClient` to provide a different `QueryClient` instance for a part - * of the application or for unit testing purposes. + * [`QueryClient`](https://tanstack.com/query/latest/docs/reference/QueryClient) for the entire application — + * it calls `provideQueryClient` internally. Use `provideQueryClient` directly to provide a different + * `QueryClient` instance for part of the application, or for unit testing. * @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - * @returns a provider object that can be used to provide the `QueryClient` instance. + * @returns A provider object that can be used to provide the `QueryClient` instance. + * + * @example + * Providing a test-only `QueryClient` in a component test, without wiring up `provideTanStackQuery`'s other + * defaults: + * ```ts + * TestBed.configureTestingModule({ + * providers: [provideQueryClient(new QueryClient())], + * }) + * ``` */ export function provideQueryClient( queryClient: QueryClient | InjectionToken, @@ -30,30 +38,28 @@ export function provideQueryClient( } /** - * Sets up providers necessary to enable TanStack Query functionality for Angular applications. - * - * Allows configuring a `QueryClient` and optional features such as developer tools. + * Sets up providers necessary to enable TanStack Query functionality for Angular applications. Allows + * configuring a `QueryClient` and optional features such as developer tools. * - * **Example - standalone** + * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start + * @see {@link withDevtools} + * @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. + * @param features - Optional features to configure additional Query functionality. + * @returns A set of providers to set up TanStack Query. * + * @example * ```ts - * import { - * provideTanStackQuery, - * QueryClient, - * } from '@tanstack/angular-query-experimental' + * import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' * * bootstrapApplication(AppComponent, { * providers: [provideTanStackQuery(new QueryClient())], * }) * ``` * - * **Example - NgModule-based** - * + * @example + * The same, in an `NgModule`-based application: * ```ts - * import { - * provideTanStackQuery, - * QueryClient, - * } from '@tanstack/angular-query-experimental' + * import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' * * @NgModule({ * declarations: [AppComponent], @@ -64,26 +70,26 @@ export function provideQueryClient( * export class AppModule {} * ``` * - * You can also enable optional developer tools by adding `withDevtools`. By - * default the tools will then be loaded when your app is in development mode. + * @example + * Enabling optional developer tools by adding `withDevtools` — by default, the tools are then loaded when + * your app is in development mode: * ```ts * import { * provideTanStackQuery, - * withDevtools + * withDevtools, * QueryClient, * } from '@tanstack/angular-query-experimental' * - * bootstrapApplication(AppComponent, - * { - * providers: [ - * provideTanStackQuery(new QueryClient(), withDevtools()) - * ] - * } - * ) + * bootstrapApplication(AppComponent, { + * providers: [provideTanStackQuery(new QueryClient(), withDevtools())], + * }) * ``` * - * **Example: using an InjectionToken** - * + * @example + * Using an `InjectionToken` for the `QueryClient` — an advanced optimization that lets TanStack Query be + * absent from the main application bundle, useful for including it on lazy-loaded routes only while still + * sharing a `QueryClient`. This is a small optimization; for most applications it's preferable to provide + * the `QueryClient` in the main application config, as in the examples above: * ```ts * export const MY_QUERY_CLIENT = new InjectionToken('', { * factory: () => new QueryClient(), @@ -92,15 +98,6 @@ export function provideQueryClient( * // In a lazy loaded route or lazy loaded component's providers array: * providers: [provideTanStackQuery(MY_QUERY_CLIENT)] * ``` - * Using an InjectionToken for the QueryClient is an advanced optimization which allows TanStack Query to be absent from the main application bundle. - * This can be beneficial if you want to include TanStack Query on lazy loaded routes only while still sharing a `QueryClient`. - * - * Note that this is a small optimization and for most applications it's preferable to provide the `QueryClient` in the main application config. - * @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - * @param features - Optional features to configure additional Query functionality. - * @returns A set of providers to set up TanStack Query. - * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start - * @see withDevtools */ export function provideTanStackQuery( queryClient: QueryClient | InjectionToken, @@ -116,9 +113,9 @@ export function provideTanStackQuery( * Sets up providers necessary to enable TanStack Query functionality for Angular applications. * * Allows configuring a `QueryClient`. + * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start * @param queryClient - A `QueryClient` instance. * @returns A set of providers to set up TanStack Query. - * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start * @deprecated Use `provideTanStackQuery` instead. */ export function provideAngularQuery(queryClient: QueryClient): Array { @@ -139,8 +136,8 @@ export interface QueryFeature { /** * Helper function to create an object that represents a Query feature. - * @param kind - - * @param providers - + * @param kind - The kind of feature, e.g. `'Devtools'`. + * @param providers - The Angular providers this feature contributes to `provideTanStackQuery`. * @returns A Query feature. */ export function queryFeature( diff --git a/packages/angular-query-experimental/src/query-options.ts b/packages/angular-query-experimental/src/query-options.ts index 4f6897618ec..d5a39e4012f 100644 --- a/packages/angular-query-experimental/src/query-options.ts +++ b/packages/angular-query-experimental/src/query-options.ts @@ -10,18 +10,43 @@ import type { } from '@tanstack/query-core' import type { CreateQueryOptions } from './types' +/** + * The options accepted by the `queryOptions` overload selected when no `initialData` is set — `data` may be + * `undefined` while the query is `pending`. + * + * @template TQueryFnData - The type your `queryFn` resolves to. + * @template TError - The type of errors your `queryFn` may throw. + * @template TData - The type `data` ends up as after `select` runs. + * @template TQueryKey - The type of your `queryKey`. + */ export type UndefinedInitialDataOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = CreateQueryOptions & { + /** + * 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 | InitialDataFunction> | NonUndefinedGuard } +/** + * The options accepted by the `queryOptions` overload selected when no `initialData` is set and `queryFn` is + * not `skipToken` — same as {@link UndefinedInitialDataOptions}, but `queryFn` may not be `skipToken`. + * + * @template TQueryFnData - The type your `queryFn` resolves to. + * @template TError - The type of errors your `queryFn` may throw. + * @template TData - The type `data` ends up as after `select` runs. + * @template TQueryKey - The type of your `queryKey`. + */ export type UnusedSkipTokenOptions< TQueryFnData = unknown, TError = DefaultError, @@ -31,12 +56,27 @@ export type UnusedSkipTokenOptions< CreateQueryOptions, '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< CreateQueryOptions['queryFn'], SkipToken | undefined > } +/** + * The options accepted by the `queryOptions` overload selected when `initialData` is set — `data` is never + * `undefined`. + * + * @template TQueryFnData - The type your `queryFn` resolves to. + * @template TError - The type of errors your `queryFn` may throw. + * @template TData - The type `data` ends up as after `select` runs. + * @template TQueryKey - The type of your `queryKey`. + */ export type DefinedInitialDataOptions< TQueryFnData = unknown, TError = DefaultError, @@ -46,32 +86,66 @@ export type DefinedInitialDataOptions< CreateQueryOptions, 'queryFn' > & { + /** + * 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 | (() => NonUndefinedGuard) + /** + * Optional here, but omitting it is only safe when no fetch will be attempted — for example with + * `enabled: false`, or when a default query function has been defined. Otherwise, an enabled query with no + * `queryFn` still tries to fetch and fails with a "Missing queryFn" error; `initialData` does not prevent this. + */ queryFn?: QueryFunction } /** - * Allows sharing and re-using query options in a type-safe way. + * You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options + * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is + * required and is the query key to generate options for. * - * The `queryKey` will be tagged with the type from `queryFn`. + * This overload is selected when `initialData` is set, so the resulting `data` is never `undefined`. * - * **Example** + * @see {@link injectQuery} to run a query with these options. + * @see [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. + * @param options - The {@link DefinedInitialDataOptions} to use — everything you can pass to `injectQuery`, + * with `initialData` set. + * @returns The same options object, typed so that `queryKey` carries the inferred data type. * - * ```ts - * const { queryKey } = queryOptions({ - * queryKey: ['key'], - * queryFn: () => Promise.resolve(5), - * // ^? Promise - * }) + * @example + * ```angular-ts + * import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' + * + * export const postsOptions = queryOptions({ + * queryKey: ['posts'], + * queryFn: fetchPosts, + * initialData: [], + * }) * - * const queryClient = new QueryClient() - * const data = queryClient.getQueryData(queryKey) - * // ^? number | undefined + * @Component({ + * selector: 'posts', + * template: ` + * + * @if (postsQuery.isError()) { + * Error: {{ postsQuery.error()?.message }} + * } + *
    + * @for (post of postsQuery.data(); track post.id) { + *
  • {{ post.title }}
  • + * } + *
+ * `, + * }) + * export class Posts { + * postsQuery = injectQuery(() => postsOptions) + * } * ``` - * @param options - The query options to tag with the type from `queryFn`. - * @returns The tagged query options. */ export function queryOptions< TQueryFnData = unknown, @@ -84,25 +158,43 @@ export function queryOptions< QueryKeyWithDataTag /** - * Allows sharing and re-using query options in a type-safe way. + * You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options + * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is + * required and is the query key to generate options for. * - * The `queryKey` will be tagged with the type from `queryFn`. + * @see {@link injectQuery} to run a query with these options. + * @see [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. + * @param options - The {@link UnusedSkipTokenOptions} to use — everything you can pass to `injectQuery`. + * @returns The same options object, typed so that `queryKey` carries the inferred data type. * - * **Example** + * @example + * A parameterized factory, so the same options object can be reused per `id`: + * ```angular-ts + * import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' * - * ```ts - * const { queryKey } = queryOptions({ - * queryKey: ['key'], - * queryFn: () => Promise.resolve(5), - * // ^? Promise + * export const postOptions = (id: string) => + * queryOptions({ + * queryKey: ['post', id], + * queryFn: () => fetchPost(id), * }) * - * const queryClient = new QueryClient() - * const data = queryClient.getQueryData(queryKey) - * // ^? number | undefined + * @Component({ + * selector: 'post', + * template: ` + * @if (postQuery.isPending()) { + * Loading... + * } @else if (postQuery.isError()) { + * Error: {{ postQuery.error()?.message }} + * } @else { + *

{{ postQuery.data().title }}

+ * } + * `, + * }) + * export class Post { + * id = signal('1') + * postQuery = injectQuery(() => postOptions(this.id())) + * } * ``` - * @param options - The query options to tag with the type from `queryFn`. - * @returns The tagged query options. */ export function queryOptions< TQueryFnData = unknown, @@ -115,25 +207,75 @@ export function queryOptions< QueryKeyWithDataTag /** - * Allows sharing and re-using query options in a type-safe way. + * You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options + * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is + * required and is the query key to generate options for. + * + * @see {@link injectQuery} to run a query with these options. + * @see [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. + * @param options - The {@link UndefinedInitialDataOptions} to use — everything you can pass to `injectQuery`. + * @returns The same options object, typed so that `queryKey` carries the inferred data type. + * @remarks This is the only overload that accepts `queryFn: skipToken`, shown below. * - * The `queryKey` will be tagged with the type from `queryFn`. + * @example + * A parameterized factory, so the same options object can be reused per `id`: + * ```angular-ts + * import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' * - * **Example** + * export const postOptions = (id: string) => + * queryOptions({ + * queryKey: ['post', id], + * queryFn: () => fetchPost(id), + * }) + * + * @Component({ + * selector: 'post', + * template: ` + * @if (postQuery.isPending()) { + * Loading... + * } @else if (postQuery.isError()) { + * Error: {{ postQuery.error()?.message }} + * } @else { + *

{{ postQuery.data().title }}

+ * } + * `, + * }) + * export class Post { + * id = signal('1') + * postQuery = injectQuery(() => postOptions(this.id())) + * } + * ``` * - * ```ts - * const { queryKey } = queryOptions({ - * queryKey: ['key'], - * queryFn: () => Promise.resolve(5), - * // ^? Promise + * @example + * A factory that disables the query, type safe, until `postId` is set: + * ```angular-ts + * import { queryOptions, skipToken, injectQuery } from '@tanstack/angular-query-experimental' + * + * export const postOptions = (postId: number | undefined) => + * queryOptions({ + * queryKey: ['post', postId], + * queryFn: postId != null ? () => fetchPost(postId) : skipToken, * }) * - * const queryClient = new QueryClient() - * const data = queryClient.getQueryData(queryKey) - * // ^? number | undefined + * @Component({ + * selector: 'post', + * template: ` + * @if (postId() == null) { + * Select a post + * } @else if (postQuery.isPending()) { + * Loading... + * } @else if (postQuery.isError()) { + * Error: {{ postQuery.error()?.message }} + * } @else { + *

{{ postQuery.data().title }}

+ * } + * `, + * }) + * export class Post { + * postId = signal(undefined) + * postQuery = injectQuery(() => postOptions(this.postId())) + * } * ``` - * @param options - The query options to tag with the type from `queryFn`. - * @returns The tagged query options. */ export function queryOptions< TQueryFnData = unknown, @@ -145,27 +287,6 @@ export function queryOptions< ): UndefinedInitialDataOptions & QueryKeyWithDataTag -/** - * Allows sharing and re-using query options in a type-safe way. - * - * The `queryKey` will be tagged with the type from `queryFn`. - * - * **Example** - * - * ```ts - * const { queryKey } = queryOptions({ - * queryKey: ['key'], - * queryFn: () => Promise.resolve(5), - * // ^? Promise - * }) - * - * const queryClient = new QueryClient() - * const data = queryClient.getQueryData(queryKey) - * // ^? number | undefined - * ``` - * @param options - The query options to tag with the type from `queryFn`. - * @returns The tagged query options. - */ export function queryOptions(options: unknown) { return options } diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query-experimental/src/types.ts index d71bec248f7..b0acd8579fc 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query-experimental/src/types.ts @@ -18,6 +18,19 @@ import type { import type { Signal } from '@angular/core' import type { MapToSignals } from './signal-proxy' +/** + * The options shared across `angular-query-experimental`'s query functions. Extends + * {@link QueryObserverOptions} from `@tanstack/query-core` as-is — unlike `react-query`, + * `angular-query-experimental` has no extra framework-specific option here. + * + * @template TQueryFnData - The type your `queryFn` resolves to. + * @template TError - The type of errors your `queryFn` may throw. + * @template TData - The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no + * `select` is used. + * @template TQueryData - The type of the data actually held in the query cache — the input to `select` and + * `placeholderData`. Defaults to, and is usually the same as, `TQueryFnData`. + * @template TQueryKey - The type of your `queryKey`. + */ export interface CreateBaseQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -32,6 +45,16 @@ export interface CreateBaseQueryOptions< TQueryKey > {} +/** + * The options accepted by `injectQuery`. Same as {@link CreateBaseQueryOptions}, minus `suspense` — which + * `angular-query-experimental` doesn't support, unlike `react-query`. + * + * @template TQueryFnData - The type your `queryFn` resolves to. + * @template TError - The type of errors your `queryFn` may throw. + * @template TData - The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no + * `select` is used. + * @template TQueryKey - The type of your `queryKey`. + */ export interface CreateQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -48,6 +71,15 @@ type CreateStatusBasedQueryResult< TError = DefaultError, > = Extract, { status: TStatus }> +/** + * The `isSuccess`/`isError`/`isPending` methods on a query result. Unlike `react-query`'s derived booleans, + * these are type-guard methods you call — `if (query.isSuccess())` — so that `query.data` narrows away + * `undefined` inside the branch, the same way `status` narrowing works on the plain object `react-query` + * returns. + * + * @template TData - The type `data` ends up as after `select` runs. + * @template TError - The type of errors your `queryFn` may throw. + */ export interface BaseQueryNarrowing { isSuccess: ( this: CreateBaseQueryResult, @@ -72,6 +104,20 @@ export interface BaseQueryNarrowing { > } +/** + * The options accepted by `injectInfiniteQuery`. Same as {@link CreateBaseQueryOptions}, minus `suspense` — + * which `angular-query-experimental` doesn't support, unlike `react-query` — extends + * {@link InfiniteQueryObserverOptions} from `@tanstack/query-core` for the infinite-query-specific options + * (`getNextPageParam`, `initialPageParam`, etc.). + * + * @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 `TQueryFnData` here, though + * `injectInfiniteQuery` itself defaults it to `InfiniteData` — the shape `data` actually has + * when no `select` is used. + * @template TQueryKey - The type of your `queryKey`. + * @template TPageParam - The type of the parameter passed to `queryFn` to fetch a given page. + */ export interface CreateInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -89,6 +135,17 @@ export interface CreateInfiniteQueryOptions< 'suspense' > {} +/** + * The result of `injectQuery` when `initialData` isn't set — `data` may be `undefined` while the query is + * `pending`. Same shape as {@link QueryObserverResult} from `@tanstack/query-core`, but value fields (like + * `data`, `error`, `status`) are exposed as a `Signal` — read them with `query.data()`, not `query.data` — + * while function fields (like `refetch`) are called directly, unchanged. `isSuccess`/`isError`/`isPending` + * are {@link BaseQueryNarrowing} type-guard methods rather than plain booleans. + * `injectInfiniteQuery` returns {@link CreateInfiniteQueryResult} instead. + * + * @template TData - The type `data` ends up as after `select` runs. + * @template TError - The type of errors your `queryFn` may throw. + */ export type CreateBaseQueryResult< TData = unknown, TError = DefaultError, @@ -96,11 +153,25 @@ export type CreateBaseQueryResult< > = BaseQueryNarrowing & MapToSignals> +/** + * The result of `injectQuery`. Same as {@link CreateBaseQueryResult}. + * + * @template TData - The type `data` ends up as after `select` runs. + * @template TError - The type of errors your `queryFn` may throw. + */ export type CreateQueryResult< TData = unknown, TError = DefaultError, > = CreateBaseQueryResult +/** + * The result of `injectQuery` when `initialData` is set — `data` is never `undefined`. Same shape as + * {@link DefinedQueryObserverResult} from `@tanstack/query-core`, but value fields are exposed as a + * `Signal` while function fields are called directly, unchanged. + * + * @template TData - The type `data` ends up as after `select` runs. + * @template TError - The type of errors your `queryFn` may throw. + */ export type DefinedCreateQueryResult< TData = unknown, TError = DefaultError, @@ -108,12 +179,29 @@ export type DefinedCreateQueryResult< > = BaseQueryNarrowing & MapToSignals> +/** + * The result of `injectInfiniteQuery` when `initialData` isn't set — `data` may be `undefined` while the + * query is `pending`. Same shape as {@link InfiniteQueryObserverResult} from `@tanstack/query-core`, but + * value fields are exposed as a `Signal` while function fields (like `fetchNextPage`) are called directly, + * unchanged. + * + * @template TData - The type `data` ends up as after `select` runs. + * @template TError - The type of errors your `queryFn` may throw. + */ export type CreateInfiniteQueryResult< TData = unknown, TError = DefaultError, > = BaseQueryNarrowing & MapToSignals> +/** + * The result of `injectInfiniteQuery` when `initialData` is set — `data` is never `undefined`. Same shape as + * {@link DefinedInfiniteQueryObserverResult} from `@tanstack/query-core`, but value fields are exposed as a + * `Signal` while function fields are called directly, unchanged. + * + * @template TData - The type `data` ends up as after `select` runs. + * @template TError - The type of errors your `queryFn` may throw. + */ export type DefinedCreateInfiniteQueryResult< TData = unknown, TError = DefaultError, @@ -123,6 +211,16 @@ export type DefinedCreateInfiniteQueryResult< >, > = MapToSignals +/** + * The options accepted by `injectMutation`. Same as {@link MutationObserverOptions} from + * `@tanstack/query-core`, minus the internal `_defaulted` flag. + * + * @template TData - The type your mutation function resolves to. + * @template TError - The type of errors your mutation function may throw. + * @template TVariables - The type of the variable passed to `mutate`/`mutateAsync`. + * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as + * their `onMutateResult` parameter — useful for optimistic-update rollback data. + */ export interface CreateMutationOptions< TData = unknown, TError = DefaultError, @@ -133,6 +231,17 @@ export interface CreateMutationOptions< '_defaulted' > {} +/** + * The type of `mutate`, as returned by `injectMutation`. Forwards the variables (and an optional per-call + * `onSuccess`/`onError`/`onSettled`) to the underlying `mutate` call. Fire-and-forget — errors are surfaced + * through the mutation result, not thrown. + * + * @template TData - The type your mutation function resolves to. + * @template TError - The type of errors your mutation function may throw. + * @template TVariables - The type of the variable passed to `mutate`. + * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as + * their `onMutateResult` parameter — useful for optimistic-update rollback data. + */ export type CreateMutateFunction< TData = unknown, TError = DefaultError, @@ -144,6 +253,16 @@ export type CreateMutateFunction< > ) => void +/** + * The type of `mutateAsync`, as returned by `injectMutation`. Similar to {@link CreateMutateFunction}, but + * returns a promise which can be awaited. + * + * @template TData - The type your mutation function resolves to. + * @template TError - The type of errors your mutation function may throw. + * @template TVariables - The type of the variable passed to `mutateAsync`. + * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as + * their `onMutateResult` parameter — useful for optimistic-update rollback data. + */ export type CreateMutateAsyncFunction< TData = unknown, TError = DefaultError, @@ -151,6 +270,17 @@ export type CreateMutateAsyncFunction< TOnMutateResult = unknown, > = MutateFunction +/** + * The pre-`Signal` shape {@link CreateMutationResult} is built from — not what `injectMutation` actually + * returns. Same as {@link MutationObserverResult} from `@tanstack/query-core`, with `mutate` narrowed to the + * fire-and-forget {@link CreateMutateFunction} signature, plus the added `mutateAsync`. + * + * @template TData - The type your mutation function resolves to. + * @template TError - The type of errors your mutation function may throw. + * @template TVariables - The type of the variable passed to `mutate`/`mutateAsync`. + * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as + * their `onMutateResult` parameter — useful for optimistic-update rollback data. + */ export type CreateBaseMutationResult< TData = unknown, TError = DefaultError, @@ -160,6 +290,9 @@ export type CreateBaseMutationResult< MutationObserverResult, { mutate: CreateMutateFunction } > & { + /** + * Similar to `mutate`, but returns a promise which can be awaited. + */ mutateAsync: CreateMutateAsyncFunction< TData, TError, @@ -181,6 +314,17 @@ type CreateStatusBasedMutationResult< type SignalFunction any> = T & Signal> +/** + * The `isSuccess`/`isError`/`isPending`/`isIdle` methods on a mutation result. Each is both a `Signal` + * (its current boolean value is read reactively without calling it) and a type-guard function you can + * call — `if (mutation.isSuccess())` — so that `mutation.data` narrows away `undefined` inside the branch. + * + * @template TData - The type your mutation function resolves to. + * @template TError - The type of errors your mutation function may throw. + * @template TVariables - The type of the variable passed to `mutate`/`mutateAsync`. + * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as + * their `onMutateResult` parameter — useful for optimistic-update rollback data. + */ export interface BaseMutationNarrowing< TData = unknown, TError = DefaultError, @@ -257,6 +401,18 @@ export interface BaseMutationNarrowing< > } +/** + * The result of `injectMutation`. Based on {@link CreateBaseMutationResult}, but value fields are exposed as + * a `Signal` — read them with `mutation.data()`, not `mutation.data` — while function fields (`mutate`, + * `mutateAsync`, `reset`) are called directly, unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are + * {@link BaseMutationNarrowing} type-guard methods rather than plain booleans. + * + * @template TData - The type your mutation function resolves to. + * @template TError - The type of errors your mutation function may throw. + * @template TVariables - The type of the variable passed to `mutate`/`mutateAsync`. + * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as + * their `onMutateResult` parameter — useful for optimistic-update rollback data. + */ export type CreateMutationResult< TData = unknown, TError = DefaultError, From 465b2f3bdc8ebf42e4d3819a4272ab999d8c92a2 Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Sun, 6 Sep 2026 00:36:46 +0900 Subject: [PATCH 2/3] docs(angular-query-experimental): address CodeRabbit review comments on JSDoc accuracy --- .../reference/functions/injectInfiniteQuery.md | 2 +- .../angular/reference/functions/injectQuery.md | 11 ++++++----- .../reference/functions/provideIsRestoring.md | 4 ++-- .../angular/reference/functions/queryOptions.md | 9 +++++---- .../DefinedInitialDataInfiniteOptions.md | 2 +- .../type-aliases/DefinedInitialDataOptions.md | 2 +- .../reference/type-aliases/QueriesOptions.md | 9 +++++---- .../reference/type-aliases/QueriesResults.md | 8 ++++---- .../src/infinite-query-options.ts | 2 +- .../src/inject-infinite-query.ts | 2 +- .../src/inject-is-restoring.ts | 4 ++-- .../src/inject-queries.ts | 13 +++++++------ .../angular-query-experimental/src/inject-query.ts | 5 +++-- .../angular-query-experimental/src/query-options.ts | 5 +++-- 14 files changed, 42 insertions(+), 36 deletions(-) diff --git a/docs/framework/angular/reference/functions/injectInfiniteQuery.md b/docs/framework/angular/reference/functions/injectInfiniteQuery.md index f5d87075b2a..7b614500e5d 100644 --- a/docs/framework/angular/reference/functions/injectInfiniteQuery.md +++ b/docs/framework/angular/reference/functions/injectInfiniteQuery.md @@ -16,7 +16,7 @@ The options for `injectInfiniteQuery` are identical to `injectQuery`, with the a additively "load more" data onto an existing set of data, or "infinite scroll". This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, -so the resulting `data` signal is never `undefined`. +so the resulting `data` signal is never `undefined` (unless a `select` narrows `TData` to include it). ### Type Parameters diff --git a/docs/framework/angular/reference/functions/injectQuery.md b/docs/framework/angular/reference/functions/injectQuery.md index d60cf1899dc..76775296e33 100644 --- a/docs/framework/angular/reference/functions/injectQuery.md +++ b/docs/framework/angular/reference/functions/injectQuery.md @@ -9,10 +9,10 @@ title: injectQuery function injectQuery(injectQueryFn, options?): DefinedCreateQueryResult; ``` -Defined in: [inject-query.ts:68](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L68) +Defined in: [inject-query.ts:69](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L69) This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the -resulting `data` signal is never `undefined`. +resulting `data` signal is never `undefined` (unless a `select` narrows `TData` to include it). ### Type Parameters @@ -52,7 +52,8 @@ Additional configuration [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`TData`, `TError`\> -The query result, typed so that `data` is never `undefined`. +The query result, typed so that `data` is never `undefined` (unless a `select` narrows `TData` to +include it). ### See @@ -93,7 +94,7 @@ export class Posts { function injectQuery(injectQueryFn, options?): CreateQueryResult; ``` -Defined in: [inject-query.ts:157](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L157) +Defined in: [inject-query.ts:158](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L158) Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. @@ -211,7 +212,7 @@ export class Posts { function injectQuery(injectQueryFn, options?): CreateQueryResult; ``` -Defined in: [inject-query.ts:184](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L184) +Defined in: [inject-query.ts:185](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L185) This overload accepts the general [CreateQueryOptions](../interfaces/CreateQueryOptions.md) shape rather than the `initialData`-aware overloads above, so whether `data` is defined can't be inferred from the call site — useful when wrapping diff --git a/docs/framework/angular/reference/functions/provideIsRestoring.md b/docs/framework/angular/reference/functions/provideIsRestoring.md index e33a3e479ae..44f5f476d3e 100644 --- a/docs/framework/angular/reference/functions/provideIsRestoring.md +++ b/docs/framework/angular/reference/functions/provideIsRestoring.md @@ -10,8 +10,8 @@ function provideIsRestoring(isRestoring): Provider; Defined in: [inject-is-restoring.ts:48](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L48) Registers a provider for the restore state read by `injectIsRestoring`. Wire this up wherever you drive a -restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can hold off -initializing queries until the restore signal flips back to `false`. +restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can defer subscribing +to their observer (avoiding a race with the restore) until the restore signal flips back to `false`. ## Parameters diff --git a/docs/framework/angular/reference/functions/queryOptions.md b/docs/framework/angular/reference/functions/queryOptions.md index f8f33e5b789..291c828cf0e 100644 --- a/docs/framework/angular/reference/functions/queryOptions.md +++ b/docs/framework/angular/reference/functions/queryOptions.md @@ -9,13 +9,14 @@ title: queryOptions function queryOptions(options): Omit, "queryFn"> & object & QueryKeyWithDataTag; ``` -Defined in: [query-options.ts:150](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L150) +Defined in: [query-options.ts:151](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L151) You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `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`. +This overload is selected when `initialData` is set, so the resulting `data` is never `undefined` (unless +a `select` narrows `TData` to include it). ### Type Parameters @@ -90,7 +91,7 @@ export class Posts { function queryOptions(options): OmitKeyof, "queryFn"> & object & QueryKeyWithDataTag; ``` -Defined in: [query-options.ts:199](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L199) +Defined in: [query-options.ts:200](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L200) You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is @@ -167,7 +168,7 @@ export class Post { function queryOptions(options): CreateQueryOptions & object & QueryKeyWithDataTag; ``` -Defined in: [query-options.ts:280](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L280) +Defined in: [query-options.ts:281](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L281) You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is diff --git a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md index 8782c6118ec..0a0f923adf6 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md @@ -10,7 +10,7 @@ type DefinedInitialDataInfiniteOptions = Omit = TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryOptionsForCreateQueries] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions<[...Tails], [...TResults, GetCreateQueryOptionsForCreateQueries], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; ``` -Defined in: [inject-queries.ts:153](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L153) +Defined in: [inject-queries.ts:154](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L154) The `queries` array accepted by `injectQueries`. Recursively unwraps each tuple element so every entry's -`queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements. An opaque array (e.g. -`unknown[]`) is returned as-is; a non-tuple array of a known element type, or a tuple past 20 elements, -falls back to a single homogeneous options type. +`queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements — past that, tuple +recursion falls back to a single homogeneous options type. An opaque array (e.g. `unknown[]`) is returned +as-is; a non-tuple array of a known element type is mapped to that element type instead, with no such +limit. ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/QueriesResults.md b/docs/framework/angular/reference/type-aliases/QueriesResults.md index ba74e04daf7..b44ac22ee82 100644 --- a/docs/framework/angular/reference/type-aliases/QueriesResults.md +++ b/docs/framework/angular/reference/type-aliases/QueriesResults.md @@ -7,12 +7,12 @@ title: QueriesResults type QueriesResults = TDepth["length"] extends MAXIMUM_DEPTH ? CreateQueryResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResult] : T extends [infer Head, ...(infer Tails)] ? QueriesResults<[...Tails], [...TResults, GetCreateQueryResult], [...TDepth, 1]> : { [K in keyof T]: GetCreateQueryResult }; ``` -Defined in: [inject-queries.ts:204](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L204) +Defined in: [inject-queries.ts:205](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L205) The result type returned by `injectQueries`, when no `combine` is provided. Mirrors [QueriesOptions](QueriesOptions.md): -each tuple element's result type is inferred individually, up to 20 elements. A non-tuple array is mapped -per-element instead, still inferring each entry individually; only past 20 elements does this fall back to -a single homogeneous [CreateQueryResult](CreateQueryResult.md) type. +each tuple element's result type is inferred individually, up to 20 elements — past that, tuple recursion +falls back to a single homogeneous [CreateQueryResult](CreateQueryResult.md) type. A non-tuple array is mapped per-element +instead, with no such limit — every entry keeps its individually inferred type regardless of array length. ## Type Parameters diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query-experimental/src/infinite-query-options.ts index 696f3a715b7..a1362d94271 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query-experimental/src/infinite-query-options.ts @@ -97,7 +97,7 @@ export type UnusedSkipTokenInfiniteOptions< /** * The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set — `data` is - * never `undefined`. + * never `undefined` (unless a `select` narrows `TData` to include it). * * @template TQueryFnData - The type of a single page, as your `queryFn` resolves it. * @template TError - The type of errors your `queryFn` may throw. diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts index 194e8a3e249..d5da3f85572 100644 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ b/packages/angular-query-experimental/src/inject-infinite-query.ts @@ -37,7 +37,7 @@ export interface InjectInfiniteQueryOptions { * additively "load more" data onto an existing set of data, or "infinite scroll". * * This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, - * so the resulting `data` signal is never `undefined`. + * so the resulting `data` signal is never `undefined` (unless a `select` narrows `TData` to include it). * * @remarks Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default * refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user diff --git a/packages/angular-query-experimental/src/inject-is-restoring.ts b/packages/angular-query-experimental/src/inject-is-restoring.ts index f73310affc5..901f5e55852 100644 --- a/packages/angular-query-experimental/src/inject-is-restoring.ts +++ b/packages/angular-query-experimental/src/inject-is-restoring.ts @@ -40,8 +40,8 @@ export function injectIsRestoring(options?: InjectIsRestoringOptions) { /** * Registers a provider for the restore state read by `injectIsRestoring`. Wire this up wherever you drive a - * restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can hold off - * initializing queries until the restore signal flips back to `false`. + * restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can defer subscribing + * to their observer (avoiding a race with the restore) until the restore signal flips back to `false`. * @param isRestoring - A readonly `Signal` that tracks the restore state. * @returns A provider for the `isRestoring` signal. */ diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts index 218c44ac8ba..57c8ae09063 100644 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ b/packages/angular-query-experimental/src/inject-queries.ts @@ -140,9 +140,10 @@ type GetCreateQueryResult = /** * The `queries` array accepted by `injectQueries`. Recursively unwraps each tuple element so every entry's - * `queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements. An opaque array (e.g. - * `unknown[]`) is returned as-is; a non-tuple array of a known element type, or a tuple past 20 elements, - * falls back to a single homogeneous options type. + * `queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements — past that, tuple + * recursion falls back to a single homogeneous options type. An opaque array (e.g. `unknown[]`) is returned + * as-is; a non-tuple array of a known element type is mapped to that element type instead, with no such + * limit. * * @template T - The type of the `queries` array as written at the call site. * @template TResults - The internal accumulator that this type builds during recursion. It is not meant to @@ -191,9 +192,9 @@ export type QueriesOptions< /** * The result type returned by `injectQueries`, when no `combine` is provided. Mirrors {@link QueriesOptions}: - * each tuple element's result type is inferred individually, up to 20 elements. A non-tuple array is mapped - * per-element instead, still inferring each entry individually; only past 20 elements does this fall back to - * a single homogeneous {@link CreateQueryResult} type. + * each tuple element's result type is inferred individually, up to 20 elements — past that, tuple recursion + * falls back to a single homogeneous {@link CreateQueryResult} type. A non-tuple array is mapped per-element + * instead, with no such limit — every entry keeps its individually inferred type regardless of array length. * * @template T - The type of the `queries` array, as inferred by {@link QueriesOptions}. * @template TResults - The internal accumulator that this type builds during recursion. It is not meant to diff --git a/packages/angular-query-experimental/src/inject-query.ts b/packages/angular-query-experimental/src/inject-query.ts index 4a6beb3e953..f1e82fae014 100644 --- a/packages/angular-query-experimental/src/inject-query.ts +++ b/packages/angular-query-experimental/src/inject-query.ts @@ -28,7 +28,7 @@ export interface InjectQueryOptions { /** * This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the - * resulting `data` signal is never `undefined`. + * resulting `data` signal is never `undefined` (unless a `select` narrows `TData` to include it). * * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries * @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like @@ -37,7 +37,8 @@ export interface InjectQueryOptions { * can pass to `injectQuery`, with `initialData` set. Similar to `computed` from Angular, this function runs * in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. * @param options - Additional configuration - * @returns The query result, typed so that `data` is never `undefined`. + * @returns The query result, typed so that `data` is never `undefined` (unless a `select` narrows `TData` to + * include it). * * @example * ```angular-ts diff --git a/packages/angular-query-experimental/src/query-options.ts b/packages/angular-query-experimental/src/query-options.ts index d5a39e4012f..7466701f664 100644 --- a/packages/angular-query-experimental/src/query-options.ts +++ b/packages/angular-query-experimental/src/query-options.ts @@ -70,7 +70,7 @@ export type UnusedSkipTokenOptions< /** * The options accepted by the `queryOptions` overload selected when `initialData` is set — `data` is never - * `undefined`. + * `undefined` (unless a `select` narrows `TData` to include it). * * @template TQueryFnData - The type your `queryFn` resolves to. * @template TError - The type of errors your `queryFn` may throw. @@ -109,7 +109,8 @@ export type DefinedInitialDataOptions< * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `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`. + * This overload is selected when `initialData` is set, so the resulting `data` is never `undefined` (unless + * a `select` narrows `TData` to include it). * * @see {@link injectQuery} to run a query with these options. * @see [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. From e7b23e7bf23c9bc29239ee25aab623d615f742b4 Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Sun, 6 Sep 2026 00:48:35 +0900 Subject: [PATCH 3/3] docs(angular-query-experimental): use 'changes' instead of 'narrows' for select's effect on TData --- .../angular/reference/functions/injectInfiniteQuery.md | 2 +- docs/framework/angular/reference/functions/injectQuery.md | 6 +++--- docs/framework/angular/reference/functions/queryOptions.md | 2 +- .../type-aliases/DefinedInitialDataInfiniteOptions.md | 2 +- .../reference/type-aliases/DefinedInitialDataOptions.md | 2 +- .../src/infinite-query-options.ts | 2 +- .../angular-query-experimental/src/inject-infinite-query.ts | 2 +- packages/angular-query-experimental/src/inject-query.ts | 6 +++--- packages/angular-query-experimental/src/query-options.ts | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/framework/angular/reference/functions/injectInfiniteQuery.md b/docs/framework/angular/reference/functions/injectInfiniteQuery.md index 7b614500e5d..d5808c3a9d3 100644 --- a/docs/framework/angular/reference/functions/injectInfiniteQuery.md +++ b/docs/framework/angular/reference/functions/injectInfiniteQuery.md @@ -16,7 +16,7 @@ The options for `injectInfiniteQuery` are identical to `injectQuery`, with the a additively "load more" data onto an existing set of data, or "infinite scroll". This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, -so the resulting `data` signal is never `undefined` (unless a `select` narrows `TData` to include it). +so the resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`). ### Type Parameters diff --git a/docs/framework/angular/reference/functions/injectQuery.md b/docs/framework/angular/reference/functions/injectQuery.md index 76775296e33..6d26438c1b6 100644 --- a/docs/framework/angular/reference/functions/injectQuery.md +++ b/docs/framework/angular/reference/functions/injectQuery.md @@ -12,7 +12,7 @@ function injectQuery(injectQueryFn, opti Defined in: [inject-query.ts:69](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L69) This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the -resulting `data` signal is never `undefined` (unless a `select` narrows `TData` to include it). +resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`). ### Type Parameters @@ -52,8 +52,8 @@ Additional configuration [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`TData`, `TError`\> -The query result, typed so that `data` is never `undefined` (unless a `select` narrows `TData` to -include it). +The query result, typed so that `data` is never `undefined` (unless a `select` changes `TData` to +include `undefined`). ### See diff --git a/docs/framework/angular/reference/functions/queryOptions.md b/docs/framework/angular/reference/functions/queryOptions.md index 291c828cf0e..19c8693ac6b 100644 --- a/docs/framework/angular/reference/functions/queryOptions.md +++ b/docs/framework/angular/reference/functions/queryOptions.md @@ -16,7 +16,7 @@ can be shared across functions and imperative APIs such as `queryClient.fetchQue 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` (unless -a `select` narrows `TData` to include it). +a `select` changes `TData` to include `undefined`). ### Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md index 0a0f923adf6..b0cef845357 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md @@ -10,7 +10,7 @@ type DefinedInitialDataInfiniteOptions = Omit