Skip to content

Conversation

@benjavicente
Copy link

@benjavicente benjavicente commented Dec 22, 2025

🎯 Changes

This is a continuation of #9893, where the main new changes added are:

  • Changed the proxy utility to pass explicitly which methods should not convert to signals. Not doing that caused signal alias like data = query.data to fail since it would read the input signal to do the check, and in Angular input signals can't be read in the component initialization.
  • Improved change detection support. Added in existing tests assertions of the fixture pending changes, and added a SSR test to showcase that the integration with pending tasks works without relaying on the HTTP client pending tasks.
  • Changed the types of inject queries to support the combine function. The angular adapter is the only one that maps some state to callable signals, so the types needed are different from the rest of adapters. I tried to match the structure of the Vue adapter.
  • Changed DefinedInitialDataOptions, removing the override of queryFn. This change fixes the select function on queries and aligns the implementation with the rest of the signal based adapters.

I also added more tests, changed some files and type tests that aren't needed in angular v19 and up, and went thought the docs to improve the consistencies and recommendations given the current implementation.

For the docs, the main improvements were removing the recommendation of discriminating with the status signal since it is not type safe in this adapter, removing examples and a note that hinted on RxJS support for query functions, and adding missing this. and signal calls on some examples.

Before reviews, I want to add new tests, specially for the createBaseQuery changes on #9893, where the value is ready after effects instead on after init. So I'm leaving this PR as draft until that.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

Release Notes

  • New Features

    • Added experimental injectQueries API for dynamic parallel query patterns.
    • Enhanced server-side rendering support with PendingTasks integration.
  • Breaking Changes

    • Requires Angular v19+ (previously v16+).
    • Observable support removed; use lastValueFrom() to convert HTTP observables to promises.
  • Documentation

    • Updated all examples with modern Angular patterns and signal-based reactive syntax.
    • Clarified query configuration, testing best practices, and dependency injection patterns.
  • Bug Fixes

    • Corrected documentation typos and outdated API references.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Dec 22, 2025

🦋 Changeset detected

Latest commit: 00ba5b4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tanstack/angular-query-experimental Minor
@tanstack/angular-query-persist-client Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Updates Angular Query experimental package to require Angular v19+, integrates Angular's native PendingTasks API, refactors tests to component-based patterns with OnPush change detection, excludes certain observer methods from signal proxies, and updates documentation examples and compatibility notes.

Changes

Cohort / File(s) Summary
Changeset
.changeset/deep-crews-open.md
Adds changeset entry for Angular Query experimental with minor release, noting Angular v19+ requirement and component effect scheduling usage.
Version requirements & installation
docs/framework/angular/installation.md, docs/framework/angular/overview.md, docs/framework/angular/quick-start.md, packages/angular-query-experimental/README.md
Updates Angular version requirement from v16+ to v19+ across installation guides and quick-start documentation. Adds provideHttpClient() to quick-start provider example.
Documentation: guides & references
docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md, docs/framework/angular/guides/caching.md, docs/framework/angular/guides/default-query-function.md, docs/framework/angular/guides/dependent-queries.md, docs/framework/angular/guides/disabling-queries.md, docs/framework/angular/guides/does-this-replace-client-state.md, docs/framework/angular/guides/important-defaults.md, docs/framework/angular/guides/infinite-queries.md, docs/framework/angular/guides/initial-query-data.md, docs/framework/angular/guides/invalidations-from-mutations.md, docs/framework/angular/guides/mutations.md, docs/framework/angular/guides/paginated-queries.md, docs/framework/angular/guides/parallel-queries.md, docs/framework/angular/guides/placeholder-query-data.md, docs/framework/angular/guides/queries.md, docs/framework/angular/guides/query-functions.md, docs/framework/angular/guides/query-invalidation.md, docs/framework/angular/guides/query-options.md, docs/framework/angular/guides/query-retries.md, docs/framework/angular/guides/testing.md, docs/framework/angular/reference/functions/injectQuery.md, docs/framework/angular/typescript.md
Comprehensive documentation updates: add @Component decorators to code examples, update import paths to @tanstack/angular-query-experimental, replace status() checks with isError()/isSuccess()/isPending(), update signal-based patterns (postId(), this.todoId()), fix typos, adjust replacement mappings for NgRx terminology, remove obsolete PendingTasks version requirement note, convert HttpClient observables to promises via lastValueFrom, update TypeScript typing guidance and module augmentation examples.
Example applications
examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts, examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts
Refactor to use query properties directly instead of private helpers; update @for track binding from variable to $index.
Package configuration
packages/angular-query-experimental/package.json, packages/angular-query-persist-client/package.json
Remove TypeScript v50–v54 test scripts; update Angular peer dependencies to v19.0.0+; add @angular/platform-server to devDependencies.
Core source: pending tasks & query base
packages/angular-query-experimental/src/pending-tasks-compat.ts, packages/angular-query-experimental/src/create-base-query.ts
Remove legacy pending-tasks-compat module entirely; refactor create-base-query to use Angular's native PendingTasks via DestroyRef, introduce observer initialization via computed/effect pattern with result tracking, add excludeFunctions parameter to signalProxy for method exclusion.
Core source: queries & mutations
packages/angular-query-experimental/src/inject-query.ts, packages/angular-query-experimental/src/inject-infinite-query.ts, packages/angular-query-experimental/src/inject-mutation.ts, packages/angular-query-experimental/src/inject-queries.ts
Update inject functions to exclude specific methods from signal conversion (refetch, fetchNextPage, etc.) via new methodsToExclude parameter; enhance type generics; replace PENDING_TASKS compat with native PendingTasks in inject-mutation; refactor injectQueries combine signature to use RawQueriesResults and introduce generic type wrappers (GenericGetDefinedOrUndefinedQueryResult, InferDataAndError).
Core source: types & signal proxy
packages/angular-query-experimental/src/types.ts, packages/angular-query-experimental/src/signal-proxy.ts, packages/angular-query-experimental/src/query-options.ts
Convert CreateBaseQueryOptions and CreateQueryOptions from interfaces to type aliases; introduce MethodKeys type to extract function property names; update MapToSignals to accept TExcludeFields parameter for excluding methods; add MethodKeys parameter to all CreateQueryResult types (CreateBaseQueryResult, DefinedCreateQueryResult, CreateInfiniteQueryResult, CreateMutationResult); update signalProxy function to accept and apply excludeFields filtering logic; redefine DefinedInitialDataOptions to include queryFn property.
Test utilities & setup
packages/angular-query-experimental/src/__tests__/test-utils.ts
Replace signal-input helpers (setSignalInputs, setFixtureSignalInputs) with registerSignalInput; introduce setupTanStackQueryTestBed for centralized TestBed initialization; add flushQueryUpdates to advance fake timers for pending notifyManager updates.
Core tests
packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts, packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts, packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts, packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts, packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts, packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts, packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts, packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts, packages/angular-query-experimental/src/__tests__/inject-queries.test.ts, packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts, packages/angular-query-experimental/src/__tests__/inject-query.test.ts, packages/angular-query-experimental/src/__tests__/mutation-options.test.ts, packages/angular-query-experimental/src/__tests__/pending-tasks-ssr.test.ts, packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts, packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts, packages/angular-query-experimental/src/__tests__/with-devtools.test.ts
Comprehensive test refactoring: replace TestBed.runInInjectionContext patterns with @Component-decorated TestComponent fixtures using OnPush change detection; adopt setupTanStackQueryTestBed for initialization; replace timer advances with flushQueryUpdates(); introduce component-based query/mutation testing; add ChangeDetectionStrategy imports; refactor type tests (test-d.ts files) with new assertions for discriminated unions and combined results; extend pending-tasks tests for SSR and offline scenarios; replace setFixtureSignalInputs with registerSignalInput for input registration.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Rationale: Large, heterogeneous refactor spanning 40+ files with multiple distinct change patterns: (1) comprehensive API signature updates affecting all public query/mutation/options types and signalProxy, (2) dense logic changes in create-base-query and types leveraging PendingTasks, LinkedSignal, and computed patterns, (3) extensive test infrastructure rewrite affecting 16 test files with new component-based patterns and OnPush change detection, (4) distributed documentation updates across 25+ guides requiring cross-file reasoning on example consistency. Mix of conceptual changes (pending task lifecycle, method exclusion) and implementation details (TestBed setup consolidation, signal access patterns) demands careful review of type safety, backward compatibility, and testing coverage alignment.

Possibly related PRs

Suggested reviewers

  • arnoud-dv
  • TkDodo

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description comprehensively covers changes, testing, and checklist items, providing clear context and meeting the template requirements.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title 'Angular v19+ pending tasks and lazy derived signal proxy initialization' accurately summarizes the main changes in the PR, which focus on Angular v19+ support, pending task improvements, and signal proxy initialization.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@benjavicente
Copy link
Author

Changed back createBaseQuery to use signals to provide the query. A test that I added seems to demonstrate that it can be used providing updates in the same macrotask, and lazy initialized for component inputs thanks to the new signal proxy implementation.

@benjavicente benjavicente marked this pull request as ready for review December 22, 2025 22:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (5)
.changeset/deep-crews-open.md (1)

5-5: Consider expanding the changeset message for clarity.

The message is brief. Consider adding more detail about the key changes (e.g., signal proxy improvements, PendingTasks integration, or removal of Angular <19 compatibility) to help consumers understand the scope of the update.

packages/angular-query-experimental/src/__tests__/with-devtools.test.ts (1)

138-138: LGTM! Consistent test utility usage.

Replacing direct timer advancement with flushQueryUpdates() provides better abstraction and makes the test suite more maintainable. This utility wraps the timer advancement logic in a semantic function name that clearly communicates intent.

Also applies to: 170-170, 190-190, 206-206, 251-251, 291-291, 323-323, 354-354, 387-387, 417-417, 443-443, 473-473, 530-530, 551-551, 581-581

packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts (1)

39-41: LGTM! Cleaner test setup.

Using setupTanStackQueryTestBed consolidates common TestBed configuration while allowing test-specific providers (ElementRef) to be added cleanly. This improves maintainability and reduces duplication across the test suite.

docs/framework/angular/quick-start.md (1)

38-38: Minor inconsistency in provider ordering between examples.

The NgModule example has provideTanStackQuery before provideHttpClient, while the bootstrapApplication example on line 22 has the opposite order. Consider aligning the order for consistency across examples.

🔎 Suggested fix
-  providers: [provideTanStackQuery(new QueryClient()), provideHttpClient()],
+  providers: [provideHttpClient(), provideTanStackQuery(new QueryClient())],
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)

540-588: Consider extracting TestBed reset pattern.

Multiple tests repeat the TestBed.resetTestingModule() + TestBed.configureTestingModule() pattern with identical providers. Consider using the setupTanStackQueryTestBed utility from test-utils.ts to reduce duplication.

🔎 Proposed refactor
   test('should handle synchronous mutation with retry', async () => {
-    TestBed.resetTestingModule()
-    TestBed.configureTestingModule({
-      providers: [
-        provideZonelessChangeDetection(),
-        provideTanStackQuery(queryClient),
-      ],
-    })
+    setupTanStackQueryTestBed(queryClient)

     const app = TestBed.inject(ApplicationRef)

Apply similar changes to other tests that reset the TestBed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee16773 and 00ba5b4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (57)
  • .changeset/deep-crews-open.md
  • docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
  • docs/framework/angular/guides/caching.md
  • docs/framework/angular/guides/default-query-function.md
  • docs/framework/angular/guides/dependent-queries.md
  • docs/framework/angular/guides/disabling-queries.md
  • docs/framework/angular/guides/does-this-replace-client-state.md
  • docs/framework/angular/guides/important-defaults.md
  • docs/framework/angular/guides/infinite-queries.md
  • docs/framework/angular/guides/initial-query-data.md
  • docs/framework/angular/guides/invalidations-from-mutations.md
  • docs/framework/angular/guides/mutations.md
  • docs/framework/angular/guides/paginated-queries.md
  • docs/framework/angular/guides/parallel-queries.md
  • docs/framework/angular/guides/placeholder-query-data.md
  • docs/framework/angular/guides/queries.md
  • docs/framework/angular/guides/query-functions.md
  • docs/framework/angular/guides/query-invalidation.md
  • docs/framework/angular/guides/query-options.md
  • docs/framework/angular/guides/query-retries.md
  • docs/framework/angular/guides/testing.md
  • docs/framework/angular/installation.md
  • docs/framework/angular/overview.md
  • docs/framework/angular/quick-start.md
  • docs/framework/angular/reference/functions/injectQuery.md
  • docs/framework/angular/typescript.md
  • examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts
  • examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts
  • packages/angular-query-experimental/README.md
  • packages/angular-query-experimental/package.json
  • packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts
  • packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts
  • packages/angular-query-experimental/src/__tests__/inject-queries.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts
  • packages/angular-query-experimental/src/__tests__/inject-query.test.ts
  • packages/angular-query-experimental/src/__tests__/mutation-options.test.ts
  • packages/angular-query-experimental/src/__tests__/pending-tasks-ssr.test.ts
  • packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts
  • packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts
  • packages/angular-query-experimental/src/__tests__/test-utils.ts
  • packages/angular-query-experimental/src/__tests__/with-devtools.test.ts
  • packages/angular-query-experimental/src/create-base-query.ts
  • packages/angular-query-experimental/src/inject-infinite-query.ts
  • packages/angular-query-experimental/src/inject-mutation.ts
  • packages/angular-query-experimental/src/inject-queries.ts
  • packages/angular-query-experimental/src/inject-query.ts
  • packages/angular-query-experimental/src/pending-tasks-compat.ts
  • packages/angular-query-experimental/src/query-options.ts
  • packages/angular-query-experimental/src/signal-proxy.ts
  • packages/angular-query-experimental/src/types.ts
  • packages/angular-query-persist-client/package.json
💤 Files with no reviewable changes (4)
  • docs/framework/angular/guides/important-defaults.md
  • docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
  • packages/angular-query-persist-client/package.json
  • packages/angular-query-experimental/src/pending-tasks-compat.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: arnoud-dv
Repo: TanStack/query PR: 9669
File: docs/framework/angular/guides/testing.md:49-56
Timestamp: 2025-09-21T00:31:02.518Z
Learning: TestBed.tick() is a valid API introduced in Angular 20 for triggering effects in unit tests, similar to ApplicationRef.tick() but for testing contexts.
📚 Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.

Applied to files:

  • packages/angular-query-experimental/src/__tests__/pending-tasks-ssr.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts
  • packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts
  • packages/angular-query-experimental/src/__tests__/test-utils.ts
  • packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts
  • docs/framework/angular/reference/functions/injectQuery.md
  • packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts
  • .changeset/deep-crews-open.md
  • docs/framework/angular/guides/query-functions.md
  • packages/angular-query-experimental/src/__tests__/with-devtools.test.ts
  • docs/framework/angular/typescript.md
  • docs/framework/angular/overview.md
  • packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
  • docs/framework/angular/guides/query-invalidation.md
  • docs/framework/angular/guides/query-retries.md
  • docs/framework/angular/guides/testing.md
  • packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts
  • packages/angular-query-experimental/src/inject-mutation.ts
  • packages/angular-query-experimental/src/__tests__/mutation-options.test.ts
  • packages/angular-query-experimental/src/create-base-query.ts
  • docs/framework/angular/guides/disabling-queries.md
  • docs/framework/angular/guides/initial-query-data.md
  • packages/angular-query-experimental/src/__tests__/inject-query.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-queries.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts
  • packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts
  • docs/framework/angular/guides/dependent-queries.md
  • packages/angular-query-experimental/src/query-options.ts
  • docs/framework/angular/guides/parallel-queries.md
📚 Learning: 2025-09-21T00:31:02.518Z
Learnt from: arnoud-dv
Repo: TanStack/query PR: 9669
File: docs/framework/angular/guides/testing.md:49-56
Timestamp: 2025-09-21T00:31:02.518Z
Learning: TestBed.tick() is a valid API introduced in Angular 20 for triggering effects in unit tests, similar to ApplicationRef.tick() but for testing contexts.

Applied to files:

  • packages/angular-query-experimental/src/__tests__/pending-tasks-ssr.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts
  • packages/angular-query-experimental/src/__tests__/test-utils.ts
  • .changeset/deep-crews-open.md
  • packages/angular-query-experimental/src/__tests__/with-devtools.test.ts
  • packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
  • docs/framework/angular/guides/testing.md
  • packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts
  • packages/angular-query-experimental/src/__tests__/mutation-options.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-query.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-queries.test.ts
  • packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts
  • packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts
📚 Learning: 2025-11-02T22:52:33.071Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts
  • packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts
  • docs/framework/angular/guides/query-functions.md
  • docs/framework/angular/overview.md
  • docs/framework/angular/guides/query-retries.md
  • packages/angular-query-experimental/src/inject-infinite-query.ts
  • packages/angular-query-experimental/src/inject-queries.ts
  • docs/framework/angular/guides/disabling-queries.md
  • packages/angular-query-experimental/src/__tests__/inject-query.test.ts
  • packages/angular-query-experimental/src/inject-query.ts
  • packages/angular-query-experimental/src/query-options.ts
  • packages/angular-query-experimental/src/types.ts
  • docs/framework/angular/guides/query-options.md
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
Repo: TanStack/query PR: 9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.

Applied to files:

  • packages/angular-query-experimental/package.json
  • docs/framework/angular/guides/query-retries.md
  • docs/framework/angular/guides/testing.md
  • packages/angular-query-experimental/src/inject-mutation.ts
  • packages/angular-query-experimental/src/create-base-query.ts
📚 Learning: 2025-08-25T19:16:59.365Z
Learnt from: justjake
Repo: TanStack/query PR: 9589
File: packages/query-core/src/queryCache.ts:132-146
Timestamp: 2025-08-25T19:16:59.365Z
Learning: The TypeScript pattern `t satisfies never` in a switch statement's default case is valid for exhaustiveness checking when all possible values of a union type are handled in the switch cases. For `typeof` operations, this ensures all 8 possible return values are covered: "string", "number", "bigint", "boolean", "symbol", "undefined", "object", "function".

Applied to files:

  • docs/framework/angular/typescript.md
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • docs/framework/angular/guides/query-invalidation.md
  • packages/angular-query-experimental/src/inject-queries.ts
  • packages/angular-query-experimental/src/inject-query.ts
  • packages/angular-query-experimental/src/query-options.ts
  • packages/angular-query-experimental/src/types.ts
🧬 Code graph analysis (18)
packages/angular-query-experimental/src/__tests__/pending-tasks-ssr.test.ts (1)
packages/angular-query-experimental/src/inject-query.ts (1)
  • injectQuery (224-232)
packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts (2)
packages/angular-query-experimental/src/inject-infinite-query.ts (1)
  • injectInfiniteQuery (115-140)
packages/query-core/src/types.ts (1)
  • InfiniteData (204-207)
packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • setupTanStackQueryTestBed (52-64)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
packages/query-core/src/queryObserver.ts (1)
  • options (379-385)
packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts (1)
packages/angular-query-experimental/src/inject-queries.ts (1)
  • injectQueries (271-381)
packages/angular-query-experimental/src/__tests__/with-devtools.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • flushQueryUpdates (70-72)
packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts (2)
packages/angular-query-experimental/src/signal-proxy.ts (1)
  • signalProxy (19-61)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • registerSignalInput (83-100)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • registerSignalInput (83-100)
packages/angular-query-experimental/src/inject-infinite-query.ts (3)
packages/query-core/src/types.ts (3)
  • DefaultError (47-51)
  • InfiniteData (204-207)
  • QueryKey (53-61)
packages/angular-query-experimental/src/types.ts (1)
  • CreateInfiniteQueryOptions (69-84)
packages/angular-query-experimental/src/signal-proxy.ts (1)
  • MethodKeys (4-6)
packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • setupTanStackQueryTestBed (52-64)
packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (2)
  • setupTanStackQueryTestBed (52-64)
  • flushQueryUpdates (70-72)
packages/angular-query-experimental/src/__tests__/mutation-options.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (2)
  • setupTanStackQueryTestBed (52-64)
  • flushQueryUpdates (70-72)
packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • registerSignalInput (83-100)
packages/angular-query-experimental/src/__tests__/inject-queries.test.ts (2)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • setupTanStackQueryTestBed (52-64)
packages/angular-query-experimental/src/inject-queries.ts (1)
  • injectQueries (271-381)
packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts (3)
packages/angular-query-experimental/src/__tests__/test-utils.ts (1)
  • setupTanStackQueryTestBed (52-64)
examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts (1)
  • Component (12-54)
packages/angular-query-experimental/src/inject-infinite-query.ts (1)
  • injectInfiniteQuery (115-140)
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
packages/angular-query-experimental/src/__tests__/test-utils.ts (2)
  • setupTanStackQueryTestBed (52-64)
  • flushQueryUpdates (70-72)
packages/angular-query-experimental/src/query-options.ts (1)
packages/angular-query-experimental/src/types.ts (1)
  • CreateQueryOptions (29-37)
packages/angular-query-experimental/src/types.ts (2)
packages/svelte-query/src/types.ts (3)
  • CreateBaseQueryOptions (24-30)
  • CreateQueryOptions (39-44)
  • CreateInfiniteQueryResult (68-71)
packages/angular-query-experimental/src/signal-proxy.ts (2)
  • MapToSignals (8-10)
  • MethodKeys (4-6)
🪛 markdownlint-cli2 (0.18.1)
docs/framework/angular/installation.md

10-10: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (96)
.changeset/deep-crews-open.md (1)

2-2: Verify the version level: should this be "major" instead of "minor"?

The PR objectives note that Angular <19 support was removed and Angular v19+ is now required. This is a breaking change for consumers using Angular <19, which typically warrants a major version bump rather than minor.

Confirm whether this changeset should be updated to reflect the breaking nature of the change.

docs/framework/angular/guides/infinite-queries.md (1)

22-22: Approve documentation examples for Angular v19 DI patterns.

The additions of projectsService = inject(ProjectsService) consistently demonstrate Angular's dependency-injection pattern in both examples. The queryFn correctly captures this via arrow function closure to access the injected service. This aligns well with the PR objective to correct examples and include proper this. references, making the documentation clearer for users implementing infinite queries with Angular v19.

Also applies to: 78-79

docs/framework/angular/guides/caching.md (1)

30-30: Typo correction approved.

The correction from "que yFn" to "queryFn" improves clarity in the documentation narrative.

examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts (1)

32-53: Nice simplification—direct query API access eliminates duplication.

The refactor from private state mirrors to direct query method calls (hasNextPage(), isFetchingNextPage(), etc.) is clean and correct. All methods are properly invoked as signals within computed(), establishing the right reactive dependencies. This aligns well with the signal-based improvements in Angular 19.

docs/framework/angular/guides/testing.md (2)

32-32: Clear guidance on test setup patterns.

The expansion of the afterEach section effectively communicates the rationale for preferring a fresh QueryClient per test over reusing and clearing, emphasizing explicit setup over "invisible globals." This aligns well with the PR's emphasis on deterministic, lifecycle-aware testing.


47-47: Add version-specific guidance for effect triggering in Angular 19 vs. 20+.

Examples at lines 47 and 140 use TestBed.tick() to trigger effects, but this API was introduced in Angular 20. Since this documentation targets Angular 19+, provide guidance for Angular 19 users (e.g., use TestBed.flushEffects() or TestBed.inject(ApplicationRef).tick()) to ensure examples work across supported versions.

docs/framework/angular/guides/disabling-queries.md (2)

16-21: Template syntax leverages Angular 19 control flow with proper signal binding.

The change from direct signal calls inside @for to binding query.data() once via @if (query.data(); as data) is a best practice for signal-based components. This avoids redundant signal reads and improves change detection efficiency.


73-73: Import path aligns with Angular Query experimental package migration.

The @tanstack/angular-query-experimental package offers a 1st-class API for using TanStack Query via Angular. All Angular guide examples consistently import injectQuery and queryOptions from @tanstack/angular-query-experimental. This import source is used uniformly across all Angular documentation examples, guides, and quick-start materials.

packages/angular-query-experimental/package.json (2)

104-105: Test coverage aligns correctly with Angular 19 requirements.

The peerDependencies now require Angular >=19.0.0, and test scripts only cover TypeScript 5.5+. This is correct: Angular 19 dropped support for TypeScript versions below 5.5, so the testing strategy appropriately covers the supported range.


91-91: @angular/platform-server belongs in devDependencies.

The package is correctly placed as a development dependency since it's used only for SSR testing with PendingTasks integration, not in runtime code. The library's runtime exports use @angular/core, HttpClient, and @angular/common/http, not @angular/platform-server.

docs/framework/angular/reference/functions/injectQuery.md (1)

12-21: LGTM! Consistent use of lastValueFrom for Observable-to-Promise conversion.

The examples correctly demonstrate wrapping Angular's HttpClient observables with lastValueFrom to return a Promise, which aligns with TanStack Query's expected queryFn return type.

docs/framework/angular/guides/paginated-queries.md (1)

38-41: Good migration to boolean signal methods.

Using query.isPending() and query.isError() instead of status-based checks aligns with the updated TypeScript narrowing guidance and improves type inference in Angular templates.

docs/framework/angular/typescript.md (2)

95-97: Clear and accurate guidance on TypeScript narrowing limitations.

The explanation correctly notes that TypeScript doesn't support discriminated unions on object methods, and the recommendation to use boolean signal methods (isSuccess(), isError(), isPending()) is the right workaround for Angular's signal-based architecture.


155-171: Improved defaultError example with practical AxiosError usage.

Using AxiosError instead of unknown as the defaultError type provides a more realistic and useful example of module augmentation for error typing.

docs/framework/angular/guides/dependent-queries.md (1)

12-23: Good update to use instance-bound methods.

Using this.email(), this.getUserByEmail, and this.getProjectsByUser correctly demonstrates class-based Angular component patterns and is consistent with the rest of the documentation updates.

packages/angular-query-experimental/src/inject-infinite-query.ts (2)

142-146: LGTM! Correctly excludes action methods from signal conversion.

The methodsToExclude array properly identifies fetchNextPage, fetchPreviousPage, and refetch as methods that should remain as regular functions rather than being wrapped in signals. This prevents early signal reads and maintains the expected imperative API for these action methods.


115-139: Clean integration with the signal proxy exclusion mechanism.

The implementation properly passes methodsToExclude to createBaseQuery, maintaining consistency with the injectQuery implementation. The generic type parameters are well-structured with appropriate defaults.

packages/angular-query-experimental/src/signal-proxy.ts (3)

4-10: Well-designed type utilities for selective signal exclusion.

The MethodKeys utility correctly extracts function property names, and the updated MapToSignals type conditionally preserves original function types for excluded fields while converting others to signals. This provides accurate type inference for consumers.


34-41: Correct use of untracked for excluded methods.

Wrapping the function call in untracked(inputSignal)[prop] ensures that calling these methods (like refetch) doesn't create a reactive dependency on the input signal. This prevents the issue of reading input signals too early during component initialization, as mentioned in the PR objectives.


47-58: Consider using untracked consistently in ownKeys handler.

The has handler at line 48 and ownKeys handler at line 51 correctly use untracked to avoid creating reactive dependencies. This is consistent and appropriate for these metadata operations.

docs/framework/angular/guides/queries.md (1)

22-24: LGTM! Good documentation improvement.

Adding the @Component decorator provides better context for the example, making it clearer that TodosComponent is a complete Angular component.

packages/angular-query-experimental/README.md (2)

81-84: LGTM! Complete NgModule example.

Adding provideHttpClient() and the AppModule export makes this a more complete, copy-pasteable example. The provideHttpClient() addition is particularly helpful since many users will need HTTP client for their query functions.


32-32: Clarify Angular version requirement discrepancy.

The README correctly reflects the package.json peer dependencies (Angular 19+). However, official TanStack Query documentation states the adapter requires Angular 16 or higher. Verify this upgrade from 16+ to 19+ is intentional and update the package.json comment or documentation if needed to explain the minimum requirement.

packages/angular-query-experimental/src/__tests__/with-devtools.test.ts (1)

16-16: LGTM! Test utility import.

Importing the centralized flushQueryUpdates utility is consistent with the test refactoring pattern used across the test suite.

docs/framework/angular/guides/does-this-replace-client-state.md (1)

10-11: LGTM! Angular-specific terminology.

Replacing React-centric state management references (Redux, MobX, Zustand) with Angular equivalents (NgRx Store, RxJS) makes the documentation more relevant and relatable for Angular developers.

packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts (1)

1-6: LGTM! Centralized test utility imports.

Switching to setupTanStackQueryTestBed reduces boilerplate and improves consistency across the test suite.

docs/framework/angular/overview.md (2)

16-16: LGTM! Angular 19+ requirement.

The version requirement update is consistent across all documentation files in this PR and aligns with the package's new minimum Angular version.


76-80: LGTM! Modern Angular Query patterns.

The updated template demonstrates best practices:

  • Using isError() and isSuccess() status methods for better type discrimination
  • Leveraging Angular 19's @let syntax for local template variables
  • Improved type narrowing by checking status before accessing data

These changes provide a better example for users to follow.

packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts (1)

1-96: LGTM! Comprehensive type coverage for discriminated unions.

The new type tests thoroughly validate:

  • Default union types for data and error signals
  • Type narrowing when checking isSuccess(), isPending(), and isError()
  • Correct typing for the select function parameter and return type

This provides strong type safety guarantees and aligns with the discriminated union improvements mentioned in the PR objectives.

packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts (1)

36-46: LGTM! Select function type coverage.

The new test validates that:

  • The select function receives the correct input type (string from queryFn)
  • The returned data signal correctly reflects the transformed type (Signal<number | undefined>)

This strengthens type safety guarantees for the select transformation feature.

packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts (1)

179-262: LGTM! Comprehensive type tests for the combine function.

These tests thoroughly cover the typing scenarios for injectQueries with the combine option:

  • Validates correct data types in the combine callback
  • Confirms refetch is callable (aligned with the methodsToExclude pattern)
  • Tests branded result types with Signal wrapper
  • Verifies initialData correctly removes undefined from the data type
docs/framework/angular/guides/initial-query-data.md (2)

84-91: LGTM! Correctly updated to component-scoped patterns.

The changes properly add this. prefix for component-scoped access to queryClient and use dynamic todoId() in the fetch path, aligning with Angular component conventions throughout the documentation.


97-108: Verify queryKey consistency across examples.

Line 99 uses queryKey: ['todos', this.todoId()] while line 83 uses queryKey: ['todo', this.todoId()] (singular vs plural). This may be intentional to demonstrate different patterns, but verify this is the intended behavior.

docs/framework/angular/guides/mutations.md (1)

246-256: LGTM! Improved component example with proper Angular conventions.

The changes correctly:

  • Add @Component decorator making it a complete Angular component example
  • Rename class to SomeComponent following PascalCase conventions
  • Use this.mutation.mutate() for proper component-scoped access
packages/angular-query-experimental/src/__tests__/pending-tasks-ssr.test.ts (2)

20-28: LGTM! Proper test lifecycle setup.

Good use of destroyPlatform() for cleanup between tests and fake timers setup/teardown. This ensures test isolation for SSR scenarios.


46-71: Well-structured SSR integration test.

The test correctly:

  • Bootstraps with zoneless change detection and server rendering
  • Configures QueryClient with retry: false to avoid flakiness
  • Uses vi.runAllTimersAsync() before awaiting the HTML promise to properly advance fake timers
  • Verifies the query data appears in the server-rendered output

This validates that Angular's PendingTasks integration works correctly with TanStack Query during SSR.

docs/framework/angular/guides/invalidations-from-mutations.md (1)

5-10: LGTM!

The documentation updates appropriately align the example with Angular conventions by adding the @Component decorator and expanding the replacement mapping for better React-to-Angular translation.

Also applies to: 30-33

docs/framework/angular/guides/query-invalidation.md (1)

11-16: LGTM!

The addition of the @Component decorator and proper Angular imports makes the example more representative of real-world Angular usage.

packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts (1)

3-6: LGTM!

The migration to shared test utilities (setupTanStackQueryTestBed and flushQueryUpdates) appropriately centralizes test setup logic and improves maintainability.

Also applies to: 15-15, 38-38

packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts (1)

3-6: LGTM!

Consistent migration to the shared test utility pattern, maintaining test behavior while improving code organization.

Also applies to: 15-15

docs/framework/angular/guides/query-options.md (1)

34-34: LGTM!

The added example effectively demonstrates how to use QueryClient methods (prefetchQuery and setQueryData) with queryOptions, providing helpful guidance for readers.

Also applies to: 36-43

docs/framework/angular/guides/query-retries.md (1)

33-37: LGTM!

The documentation correctly updates to use provideTanStackQuery and bootstrapApplication, aligning with the Angular Query integration pattern.

Also applies to: 54-54

packages/angular-query-experimental/src/__tests__/mutation-options.test.ts (1)

11-11: LGTM!

Consistent application of the test utilities pattern across all test cases, replacing direct timer manipulation with the abstracted flushQueryUpdates() helper.

Also applies to: 19-19, 58-58, 78-78, 106-106, 134-134

packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts (1)

22-67: LGTM!

The component-based testing approach with ChangeDetectionStrategy.OnPush provides more realistic test coverage and aligns well with the PR's goal of testing Angular Query in component contexts.

docs/framework/angular/guides/query-functions.md (1)

12-84: LGTM! Documentation correctly demonstrates signal-based query patterns.

The examples consistently show signal function call syntax (e.g., todoId(), this.todoId()) for reactive query keys and query functions, which aligns with Angular's signal-based reactivity model and the broader changes in this PR.

packages/angular-query-experimental/src/__tests__/inject-queries.test.ts (3)

19-25: LGTM! Test setup properly uses fake timers.

The addition of vi.useFakeTimers({ shouldAdvanceTime: true }) in beforeEach and vi.useRealTimers() in afterEach ensures proper test isolation and timer control for async operations.


83-158: Excellent test coverage for the combine function.

This test thoroughly validates:

  • Combining multiple query results into a custom shape
  • The refetch method from the combined result
  • Progressive data updates (empty → partial → complete)
  • 5 distinct snapshots showing data progression through refetch cycles

The test correctly verifies both the combined data string format and the refetch functionality.


160-234: Comprehensive test for dynamic queries array.

This test validates important reactive behaviors:

  • Dynamic addition/removal of queries via signal updates
  • Correct data mapping and aggregation
  • Multiple change detection cycles (7 and 9 snapshots respectively)
  • Edge case of empty queries array

The test correctly exercises the reactive nature of the queries array and verifies that the UI updates appropriately.

packages/angular-query-experimental/src/query-options.ts (1)

44-48: LGTM! Type simplification enables select function support.

Removing the Omit<..., 'queryFn'> wrapper and using CreateQueryOptions directly reintroduces the queryFn property into DefinedInitialDataOptions. This change aligns with other signal-based adapters and fixes the select function on queries as noted in the PR objectives.

docs/framework/angular/guides/default-query-function.md (1)

31-54: LGTM! Documentation correctly demonstrates component-based query usage.

The addition of @Component decorators and the use of Angular 19's input() signal pattern make the examples more complete and realistic. The transition from postIdSignal() to postId() is consistent with the signal input API.

packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts (1)

161-181: LGTM! Host component pattern properly tests signal inputs.

The introduction of HostComponent with registerSignalInput correctly enables testing of components with required signal inputs in Angular's new testing paradigm. The use of nativeElement.querySelectorAll for DOM queries is appropriate for this test scenario.

packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts (2)

18-18: Note the exclusion mechanism.

The signalProxy now accepts a second argument ['fn'] to specify which fields should NOT be converted to signals. This is a key change enabling the refactored proxy behavior described in the PR objectives.


63-95: Excellent component fixture tests for signal proxy behavior.

These tests thoroughly validate:

  • Fields are generated lazily after first change detection
  • Excluded fields (fn) remain functions while others become signals
  • Short accessors correctly reference the proxy fields
  • Proxy fields reactively update when the underlying signal changes

The tests properly exercise the signal proxy in a realistic Angular component context with OnPush change detection.

packages/angular-query-experimental/src/inject-query.ts (1)

230-234: LGTM! Method exclusion mechanism correctly implemented.

The introduction of methodsToExclude with ['refetch'] ensures that the refetch method remains a function rather than being converted to a signal. This prevents issues with reading input signals too early (as mentioned in PR objectives) and maintains the expected API surface where refetch is called as a function.

The type safety via Array<MethodKeys<QueryObserverResult>> ensures only valid method names can be excluded.

docs/framework/angular/guides/parallel-queries.md (1)

20-62: LGTM! Documentation examples correctly demonstrate parallel queries.

The addition of @Component decorators makes the examples more complete and realistic. The experimental notice for injectQueries appropriately sets user expectations.

packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (3)

1-15: LGTM!

Import updates are appropriate: ChangeDetectionStrategy for OnPush components, firstValueFrom for RxJS observable handling, and registerSignalInput from test utilities.


302-354: LGTM!

The host-wrapper pattern for testing signal inputs is well-structured. Using registerSignalInput to mark the input as signal-based before creating the fixture ensures proper Angular change detection behavior.


661-704: LGTM!

The optimistic updates test properly validates the pending tasks lifecycle:

  1. Verifies optimistic update applies synchronously
  2. Confirms app.isStable is false while mutation is pending
  3. Verifies completion after whenStable() resolves

This effectively tests the PendingTasks integration for SSR and stability tracking.

packages/angular-query-experimental/src/__tests__/test-utils.ts (3)

48-64: LGTM!

setupTanStackQueryTestBed provides a clean abstraction for test setup, properly accepting optional additional providers via EnvironmentProviders | Provider union.


66-72: LGTM!

Clear documentation explaining why advancing timers by 0 is needed for TanStack Query's notifyManager updates.


76-99: LGTM with note on internal API usage.

The registerSignalInput function correctly documents its temporary nature using the theta (ɵ) prefix, indicating Angular's experimental APIs. The error handling for missing component definitions is appropriate. The ɵcmp and ɵdir fields are standard component/directive definitions generated by Angular's Ivy compiler, and while internal, they remain valid in Angular 19. The comment about Angular 21 CLI supporting Vitest compilation is accurate—Vitest is now promoted to stable as the default test runner in Angular v21.

docs/framework/angular/guides/placeholder-query-data.md (3)

10-19: LGTM!

The example now properly shows the Angular component wrapper around the injectQuery usage.


27-40: LGTM!

Good example demonstrating the reactive pattern with signal() for todoId, showing how both queryKey and queryFn can depend on the signal value.


45-65: LGTM!

Good example showing input.required() usage with queries and how to access cached data from QueryClient for placeholder data.

packages/angular-query-experimental/src/inject-mutation.ts (4)

1-11: LGTM!

Import change from custom PENDING_TASKS to Angular's native PendingTasks is the right approach for Angular 19+ compatibility.


84-98: LGTM!

The pending task lifecycle helpers are well-guarded:

  • startPendingTask prevents double-start with !taskCleanupRef && !destroyed
  • stopPendingTask properly nullifies the ref after invoking cleanup

141-181: LGTM!

The subscriber callback properly:

  1. Guards against destroyed state before processing
  2. Tracks pending tasks based on isPending state
  3. Cleans up in correct order: set destroyed=true, stop pending task, then unsubscribe

196-200: LGTM!

The excludeFunctions parameter correctly lists the imperative methods (mutate, mutateAsync, reset) that should remain callable functions rather than being wrapped as signals.

packages/angular-query-experimental/src/create-base-query.ts (7)

1-25: LGTM!

Import updates properly bring in Angular's native APIs (DestroyRef, PendingTasks, linkedSignal) and the necessary type imports for the refactored implementation.


86-93: LGTM!

The lazy initialization pattern is well-designed:

  • computed() creates the observer only when first accessed
  • untracked(defaultedOptionsSignal) prevents the computed from tracking option changes
  • The separate effect handles option synchronization after creation

95-127: LGTM!

The result tracking implementation correctly integrates with TanStack Query's property tracking optimization. The void value pattern (line 125) idiomatically signals that the access is intentional for tracking purposes.


180-194: LGTM!

The dual cleanup strategy is robust:

  • Effect's onCleanup handles normal effect lifecycle cleanup
  • destroyRef.onDestroy ensures cleanup even if effect cleanup doesn't run (edge cases)

The isRestoring() guard correctly prevents subscription during hydration/restore scenarios.


196-199: LGTM!

Using resultSignal.asReadonly() properly encapsulates the signal state, and the excludeFunctions parameter correctly propagates to signalProxy.


169-178: linkedSignal is available in Angular 19 (experimental) and became stable in Angular 20.

LinkedSignal was introduced in Angular Version 19, though it was promoted to stable in Angular 20. The code using linkedSignal is compatible with Angular 19 but will have experimental status at that version level.


129-167: The destroyed check timing is correct and already verified by tests.

The destroyed check at line 144 prevents stale updates by executing inside the queued microtask before state modifications. Test coverage in pending-tasks.test.ts explicitly validates this behavior with dedicated component destruction tests that confirm pending tasks are cleaned up and Angular stability is restored when components with active queries or mutations are destroyed.

packages/angular-query-experimental/src/__tests__/inject-query.test.ts (7)

1-31: LGTM! Imports properly updated for component-based testing.

The imports now include ChangeDetectionStrategy, NgZone, and registerSignalInput to support the new component-wrapped test approach with OnPush change detection.


53-260: Well-structured type inference test with comprehensive coverage.

The refactoring to a component-based approach allows all type inference tests to run within a proper Angular injection context. The tests cover:

  • Unspecified queryFn defaulting to unknown
  • Explicit generic type parameters
  • Union types, wrapped queries, and various queryFn signatures

578-619: Proper error handling cleanup for uncaughtException listener.

The error handling tests correctly manage the process.on('uncaughtException') listener lifecycle with a try/finally block ensuring cleanup even if the test fails. This prevents listener leaks across tests.


726-747: Good test for effect ordering with query injection.

This test validates an important edge case: effects registered before injectQuery should be able to safely read query data without throwing. The spy correctly verifies the initial undefined state.


749-785: Excellent test for synchronous input availability.

This test validates that when setInput is called before change detection, the query can immediately access the input signal value and return cached data. This is important for avoiding unnecessary pending states with pre-populated cache.


827-845: Good test for same-macrotask data updates.

Uses TestBed.tick() to trigger effects (valid Angular 20+ API) and validates that query data updates are reflected within the same macrotask after flushing microtasks. Based on learnings, TestBed.tick() is the appropriate API for triggering effects in unit tests.


884-914: Validates PendingTasks integration correctly.

This test ensures that whenStable() waits for query completion via the PendingTasks integration. The pattern of advancing timers and then awaiting whenStable() correctly tests the async coordination.

packages/angular-query-experimental/src/inject-queries.ts (4)

95-111: Clean generic wrapper for initialData type resolution.

GenericGetDefinedOrUndefinedQueryResult elegantly abstracts the initialData logic to work with any result type pair, eliminating code duplication between GetCreateQueryResult and GetQueryObserverResult.


113-143: Well-structured type inference for data and error types.

InferDataAndError consolidates the type inference logic shared between signal-wrapped results and raw observer results, following the same three-part pattern (object form, tuple form, queryFn inference) used in GetCreateQueryOptionsForCreateQueries.


236-264: Appropriate type change for combine callback.

RawQueriesResults correctly maps to plain QueryObserverResult types for the combine function, as the combine callback receives raw observer results rather than signal-proxied values. This aligns with how other adapters (Vue) handle the combine function.


374-379: The promise property needs special handling, but cannot be excluded via the existing mechanism.

QueryObserverResult does have a promise property (type Promise<TData>) that is currently wrapped as Signal<Promise<TData>> by signalProxy. However, it cannot be added to the ['refetch'] exclusion list because signalProxy only accepts function properties in its excludeFields parameter—the type MethodKeys<T> explicitly filters to functions only.

This creates an issue: the promise property violates TanStack Query's "stable promise" contract (documented as requiring experimental_prefetchInRender and meant for React.use()), since wrapping it as a signal makes it reactive rather than stable. Users must call .promise() to access the wrapped signal, breaking compatibility with the documented usage pattern.

Resolving this requires expanding signalProxy to support excluding non-function properties, or implementing alternative handling for Promise-type properties specifically.

packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (5)

15-42: Good test setup with centralized configuration.

Using setupTanStackQueryTestBed and resetting onlineManager.setOnline(true) in afterEach ensures consistent test isolation. The helper reduces boilerplate and ensures consistent module configuration across tests.


219-265: Excellent test for offline-start edge case.

This test validates a subtle but important scenario: when a query initializes while offline with networkMode: 'online', it goes directly to paused without ever reaching fetching. The test correctly verifies that PendingTasks still blocks stability until the app comes back online and the query completes.


400-448: Well-structured concurrent queries test.

Using a single component with multiple query properties provides clean access via fixture.componentInstance and properly tests that all concurrent queries complete before whenStable() resolves.


537-542: Clean provider composition with setupTanStackQueryTestBed.

The providers option correctly layers HttpClient testing providers on top of the base TanStack Query configuration, maintaining separation of concerns.


356-392: fixture.isStable() appropriately checks fixture-level pending tasks.

The destruction tests correctly use fixture.isStable() and fixture.whenStable() to verify pending tasks cleanup. ComponentFixture.isStable() returns !this.pendingTasks.hasPendingTasks, and TanStack Query's inject* functions integrate with PendingTasks which ensures the framework is aware of in-progress queries and mutations. This pattern is explicitly documented as the recommended approach for testing query/mutation completion.

packages/angular-query-experimental/src/types.ts (5)

19-19: Appropriate import addition for MethodKeys.

Importing MethodKeys alongside MapToSignals enables the type system to correctly identify and exclude method properties from signal conversion.


21-37: Simplified type aliases align with Svelte adapter pattern.

Changing CreateBaseQueryOptions and CreateQueryOptions from interfaces to type aliases matches the pattern used in packages/svelte-query/src/types.ts (as shown in the relevant code snippets), promoting consistency across framework adapters.


86-94: Correct MapToSignals usage with MethodKeys.

The pattern MapToSignals<T, MethodKeys<T>> ensures that method properties (like refetch, promise) remain as callable functions rather than being wrapped in signals. The OmitKeyof<TState, keyof BaseQueryNarrowing, 'safely'> correctly excludes the narrowing predicates before applying the transformation.


266-282: Mutation result type correctly updated.

CreateMutationResult properly applies MapToSignals with MethodKeys while excluding BaseMutationNarrowing predicates, maintaining consistency with the query result types.


111-130: Consistent MethodKeys pattern for infinite query results.

Both CreateInfiniteQueryResult and DefinedCreateInfiniteQueryResult correctly apply the MethodKeys pattern. Note that DefinedCreateInfiniteQueryResult doesn't include BaseQueryNarrowing in its definition, which differs from the non-infinite variants.

Verify this is intentional - the defined infinite query result type does not include the isSuccess/isError/isPending narrowing predicates:

#!/bin/bash
# Check if DefinedCreateInfiniteQueryResult should include BaseQueryNarrowing
rg -n "DefinedCreateInfiniteQueryResult" --type=ts packages/angular-query-experimental/src/

Comment on lines +35 to +47
userIds = injectQuery(() => ({
queryKey: ['users'],
queryFn: getUserData,
select: (users) => users.map((user) => user.id),
}))

// Then get the users messages
userQueries = injectQueries(() => ({
queries: (this.userIds() ?? []).map((userId) => ({
queryKey: ['user', userId],
queryFn: () => getUserById(userId),
})),
}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Incorrect data access pattern in injectQueries example.

Line 43 uses this.userIds() but userIds is the query result object from injectQuery, not a signal of the data. To access the selected user IDs, it should be this.userIds.data().

🔎 Proposed fix
 // Then get the users messages
 userQueries = injectQueries(() => ({
-  queries: (this.userIds() ?? []).map((userId) => ({
+  queries: (this.userIds.data() ?? []).map((userId) => ({
     queryKey: ['user', userId],
     queryFn: () => getUserById(userId),
   })),
 }))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
userIds = injectQuery(() => ({
queryKey: ['users'],
queryFn: getUserData,
select: (users) => users.map((user) => user.id),
}))
// Then get the users messages
userQueries = injectQueries(() => ({
queries: (this.userIds() ?? []).map((userId) => ({
queryKey: ['user', userId],
queryFn: () => getUserById(userId),
})),
}))
userIds = injectQuery(() => ({
queryKey: ['users'],
queryFn: getUserData,
select: (users) => users.map((user) => user.id),
}))
// Then get the users messages
userQueries = injectQueries(() => ({
queries: (this.userIds.data() ?? []).map((userId) => ({
queryKey: ['user', userId],
queryFn: () => getUserById(userId),
})),
}))
🤖 Prompt for AI Agents
In docs/framework/angular/guides/dependent-queries.md around lines 35 to 47, the
injectQueries example incorrectly calls this.userIds() (userIds is a query
result object), so update the code to read the selected IDs from the query
result by using this.userIds.data() when mapping; ensure you also keep a safe
fallback like (this.userIds.data() ?? []) so the map runs on an array.

Comment on lines +71 to 75
@Component({
// ...
})
export class PaginationExampleComponent {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate @Component decorator appears to be unintentional.

There's already a complete @Component decorator with the full template at lines 26-70. This second, empty @Component({ // ... }) block creates invalid Angular code since a class cannot have two decorators of the same type.

🔎 Proposed fix
     </div>
   `,
 })
-
-@Component({
-  // ...
-})
 export class PaginationExampleComponent {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Component({
// ...
})
export class PaginationExampleComponent {
`,
})
export class PaginationExampleComponent {
🤖 Prompt for AI Agents
In docs/framework/angular/guides/paginated-queries.md around lines 71 to 75
there is a duplicate, empty @Component decorator applied to
PaginationExampleComponent which is invalid; remove the second @Component({ //
... }) block so the class only has the original complete @Component decorator
(lines ~26-70) and ensure the class definition follows that single decorator.

[//]: # 'DynamicParallelIntro'
[//]: # 'Example2'

> IMPORTANT: `injectQueries` is experimental and is provided in it's own entry point
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor grammatical correction needed.

Change "it's own entry point" to "its own entry point" (possessive form, not contraction).

🔎 Proposed fix
-> IMPORTANT: `injectQueries` is experimental and is provided in it's own entry point
+> IMPORTANT: `injectQueries` is experimental and is provided in its own entry point
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> IMPORTANT: `injectQueries` is experimental and is provided in it's own entry point
> IMPORTANT: `injectQueries` is experimental and is provided in its own entry point
🤖 Prompt for AI Agents
In docs/framework/angular/guides/parallel-queries.md at line 44, the phrase
"it's own entry point" uses the contraction "it's" incorrectly; change it to the
possessive "its own entry point" to correct the grammar.

### NPM

_Angular Query is compatible with Angular v16 and higher_
_Angular Query is compatible with Angular v19 and higher_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use blockquote format instead of emphasis for the compatibility note.

The compatibility update to Angular v19+ is correct and aligns with the PR objectives. However, the current emphasis formatting using underscores triggers a markdown linting rule (MD036) that discourages using emphasis as a heading/callout. Use a blockquote format instead for better semantic clarity and linting compliance.

🔎 Proposed fix using blockquote format
-_Angular Query is compatible with Angular v19 and higher_
+> Angular Query is compatible with Angular v19 and higher
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_Angular Query is compatible with Angular v19 and higher_
> Angular Query is compatible with Angular v19 and higher
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

10-10: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🤖 Prompt for AI Agents
In docs/framework/angular/installation.md at line 10, the compatibility note
currently uses emphasis underscores ("_Angular Query is compatible with Angular
v19 and higher_") which triggers MD036; replace that emphasis with a markdown
blockquote (prefix the line with "> ") so the note becomes a proper callout and
satisfies markdown linting.

<button (click)="addItem()">Create</button>
<ul>
@for (task of tasks.data(); track task) {
@for (task of tasks.data(); track $index) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Revert to tracking by task value for correct optimistic update behavior.

Tracking by $index instead of the task value can cause incorrect DOM updates during optimistic updates and rollbacks. When an optimistically added item is rolled back, Angular may incorrectly reuse DOM nodes because it tracks by position rather than identity, potentially displaying stale or wrong data.

For dynamic lists with add/remove operations—especially in an optimistic updates example—tracking by a stable identifier or the value itself ensures correct change detection.

🔎 Proposed fix
-          @for (task of tasks.data(); track $index) {
+          @for (task of tasks.data(); track task) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@for (task of tasks.data(); track $index) {
@for (task of tasks.data(); track task) {
🤖 Prompt for AI Agents
In
examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts
around line 39, the ng-for/loop is tracking list items by $index which breaks
optimistic add/remove/rollback behavior; update the loop to track by the task
identity (preferably a stable id like task.id, or the task value if no id
exists) so Angular/Svelte will use item identity instead of position when
diffing and DOM updates will reflect correct optimistic rollbacks.

},
)

TestBed.tick()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat packages/angular-query-experimental/package.json | jq '.peerDependencies."@angular/core"'

Repository: TanStack/query

Length of output: 68


TestBed.tick() requires Angular 20, but peer dependency allows Angular 19.

TestBed.tick() was introduced in Angular 20, but the package declares @angular/core >= 19.0.0 as a peer dependency. Using TestBed.tick() will fail on Angular 19. Either update the peer dependency to require Angular 20+ or switch to an alternative approach compatible with Angular 19 (e.g., TestBed.flushEffects() for Angular 19, with a version check if needed).

🤖 Prompt for AI Agents
In
packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts
around line 99, the test calls TestBed.tick() which is only available in Angular
20+, but the package peer dependency allows Angular 19; replace the
TestBed.tick() call with a compatible alternative or gate it behind a
runtime/version check: either (A) bump the peer dependency to require
@angular/core >=20.0.0 and keep TestBed.tick(), or (B) change the test to use
TestBed.flushEffects() (or another Angular 19-compatible API) and add a small
runtime feature-detect/version check so the test calls flushEffects() on Angular
19 and tick() on Angular 20+, ensuring the test runs across the declared peer
dependency range.

@benjavicente benjavicente changed the title Angular 19 Angular v19+ pending tasks and lazy derived signal proxy initialization Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants