From 0d28e0adeab9afe13c17dde05e7497a5fab4ea43 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 28 May 2025 22:05:01 -0700 Subject: [PATCH 01/21] refactor(core): add generic utilities for resolving value-or-function patterns, replace specialized `resolveStaleTime` and `resolveEnabled` This commit introduces `isFunctionVariant()` and `resolveValueOrFunction()` utilities to replace repetitive `typeof === 'function'` checks throughout the codebase, consolidating the common "value or function that computes value" pattern. --- packages/query-core/src/query.ts | 15 +-- packages/query-core/src/queryClient.ts | 8 +- packages/query-core/src/queryObserver.ts | 63 ++++++------ packages/query-core/src/retryer.ts | 7 +- packages/query-core/src/utils.ts | 118 +++++++++++++++++------ 5 files changed, 132 insertions(+), 79 deletions(-) diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index e6ad4ca4ee..05c34bab69 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -2,7 +2,7 @@ import { ensureQueryFn, noop, replaceData, - resolveEnabled, + resolveValueOrFunction, skipToken, timeUntilStale, } from './utils' @@ -15,7 +15,6 @@ import type { CancelOptions, DefaultError, FetchStatus, - InitialDataFunction, OmitKeyof, QueryFunction, QueryFunctionContext, @@ -255,7 +254,8 @@ export class Query< isActive(): boolean { return this.observers.some( - (observer) => resolveEnabled(observer.options.enabled, this) !== false, + (observer) => + resolveValueOrFunction(observer.options.enabled, this) !== false, ) } @@ -659,17 +659,12 @@ function getDefaultState< >( options: QueryOptions, ): QueryState { - const data = - typeof options.initialData === 'function' - ? (options.initialData as InitialDataFunction)() - : options.initialData + const data = resolveValueOrFunction(options.initialData) const hasData = data !== undefined const initialDataUpdatedAt = hasData - ? typeof options.initialDataUpdatedAt === 'function' - ? (options.initialDataUpdatedAt as () => number | undefined)() - : options.initialDataUpdatedAt + ? resolveValueOrFunction(options.initialDataUpdatedAt) : 0 return { diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 3406bbf7d2..8ffcbe659f 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -4,7 +4,7 @@ import { hashQueryKeyByOptions, noop, partialMatchKey, - resolveStaleTime, + resolveValueOrFunction, skipToken, } from './utils' import { QueryCache } from './queryCache' @@ -156,7 +156,9 @@ export class QueryClient { if ( options.revalidateIfStale && - query.isStaleByTime(resolveStaleTime(defaultedOptions.staleTime, query)) + query.isStaleByTime( + resolveValueOrFunction(defaultedOptions.staleTime, query), + ) ) { void this.prefetchQuery(defaultedOptions) } @@ -364,7 +366,7 @@ export class QueryClient { const query = this.#queryCache.build(this, defaultedOptions) return query.isStaleByTime( - resolveStaleTime(defaultedOptions.staleTime, query), + resolveValueOrFunction(defaultedOptions.staleTime, query), ) ? query.fetch(defaultedOptions) : Promise.resolve(query.state.data as TData) diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 174dc72bf5..4011ddf13b 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -8,8 +8,7 @@ import { isValidTimeout, noop, replaceData, - resolveEnabled, - resolveStaleTime, + resolveValueOrFunction, shallowEqualObjects, timeUntilStale, } from './utils' @@ -157,8 +156,10 @@ export class QueryObserver< this.options.enabled !== undefined && typeof this.options.enabled !== 'boolean' && typeof this.options.enabled !== 'function' && - typeof resolveEnabled(this.options.enabled, this.#currentQuery) !== - 'boolean' + typeof resolveValueOrFunction( + this.options.enabled, + this.#currentQuery, + ) !== 'boolean' ) { throw new Error( 'Expected enabled to be a boolean or a callback that returns a boolean', @@ -201,10 +202,10 @@ export class QueryObserver< if ( mounted && (this.#currentQuery !== prevQuery || - resolveEnabled(this.options.enabled, this.#currentQuery) !== - resolveEnabled(prevOptions.enabled, this.#currentQuery) || - resolveStaleTime(this.options.staleTime, this.#currentQuery) !== - resolveStaleTime(prevOptions.staleTime, this.#currentQuery)) + resolveValueOrFunction(this.options.enabled, this.#currentQuery) !== + resolveValueOrFunction(prevOptions.enabled, this.#currentQuery) || + resolveValueOrFunction(this.options.staleTime, this.#currentQuery) !== + resolveValueOrFunction(prevOptions.staleTime, this.#currentQuery)) ) { this.#updateStaleTimeout() } @@ -215,8 +216,8 @@ export class QueryObserver< if ( mounted && (this.#currentQuery !== prevQuery || - resolveEnabled(this.options.enabled, this.#currentQuery) !== - resolveEnabled(prevOptions.enabled, this.#currentQuery) || + resolveValueOrFunction(this.options.enabled, this.#currentQuery) !== + resolveValueOrFunction(prevOptions.enabled, this.#currentQuery) || nextRefetchInterval !== this.#currentRefetchInterval) ) { this.#updateRefetchInterval(nextRefetchInterval) @@ -344,7 +345,7 @@ export class QueryObserver< #updateStaleTimeout(): void { this.#clearStaleTimeout() - const staleTime = resolveStaleTime( + const staleTime = resolveValueOrFunction( this.options.staleTime, this.#currentQuery, ) @@ -368,9 +369,10 @@ export class QueryObserver< #computeRefetchInterval() { return ( - (typeof this.options.refetchInterval === 'function' - ? this.options.refetchInterval(this.#currentQuery) - : this.options.refetchInterval) ?? false + resolveValueOrFunction( + this.options.refetchInterval, + this.#currentQuery, + ) ?? false ) } @@ -381,7 +383,8 @@ export class QueryObserver< if ( isServer || - resolveEnabled(this.options.enabled, this.#currentQuery) === false || + resolveValueOrFunction(this.options.enabled, this.#currentQuery) === + false || !isValidTimeout(this.#currentRefetchInterval) || this.#currentRefetchInterval === 0 ) { @@ -489,15 +492,11 @@ export class QueryObserver< skipSelect = true } else { // compute placeholderData - placeholderData = - typeof options.placeholderData === 'function' - ? ( - options.placeholderData as unknown as PlaceholderDataFunction - )( - this.#lastQueryWithDefinedData?.state.data, - this.#lastQueryWithDefinedData as any, - ) - : options.placeholderData + placeholderData = resolveValueOrFunction( + options.placeholderData, + this.#lastQueryWithDefinedData?.state.data, + this.#lastQueryWithDefinedData as any, + ) } if (placeholderData !== undefined) { @@ -660,9 +659,7 @@ export class QueryObserver< const { notifyOnChangeProps } = this.options const notifyOnChangePropsValue = - typeof notifyOnChangeProps === 'function' - ? notifyOnChangeProps() - : notifyOnChangeProps + resolveValueOrFunction(notifyOnChangeProps) if ( notifyOnChangePropsValue === 'all' || @@ -740,7 +737,7 @@ function shouldLoadOnMount( options: QueryObserverOptions, ): boolean { return ( - resolveEnabled(options.enabled, query) !== false && + resolveValueOrFunction(options.enabled, query) !== false && query.state.data === undefined && !(query.state.status === 'error' && options.retryOnMount === false) ) @@ -764,8 +761,8 @@ function shouldFetchOn( (typeof options)['refetchOnWindowFocus'] & (typeof options)['refetchOnReconnect'], ) { - if (resolveEnabled(options.enabled, query) !== false) { - const value = typeof field === 'function' ? field(query) : field + if (resolveValueOrFunction(options.enabled, query) !== false) { + const value = resolveValueOrFunction(field, query) return value === 'always' || (value !== false && isStale(query, options)) } @@ -780,7 +777,7 @@ function shouldFetchOptionally( ): boolean { return ( (query !== prevQuery || - resolveEnabled(prevOptions.enabled, query) === false) && + resolveValueOrFunction(prevOptions.enabled, query) === false) && (!options.suspense || query.state.status !== 'error') && isStale(query, options) ) @@ -791,8 +788,8 @@ function isStale( options: QueryObserverOptions, ): boolean { return ( - resolveEnabled(options.enabled, query) !== false && - query.isStaleByTime(resolveStaleTime(options.staleTime, query)) + resolveValueOrFunction(options.enabled, query) !== false && + query.isStaleByTime(resolveValueOrFunction(options.staleTime, query)) ) } diff --git a/packages/query-core/src/retryer.ts b/packages/query-core/src/retryer.ts index baa93aa5b4..b8573445cc 100644 --- a/packages/query-core/src/retryer.ts +++ b/packages/query-core/src/retryer.ts @@ -1,7 +1,7 @@ import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { pendingThenable } from './thenable' -import { isServer, sleep } from './utils' +import { isServer, resolveValueOrFunction, sleep } from './utils' import type { CancelOptions, DefaultError, NetworkMode } from './types' // TYPES @@ -166,10 +166,7 @@ export function createRetryer( // Do we need to retry the request? const retry = config.retry ?? (isServer ? 0 : 3) const retryDelay = config.retryDelay ?? defaultRetryDelay - const delay = - typeof retryDelay === 'function' - ? retryDelay(failureCount, error) - : retryDelay + const delay = resolveValueOrFunction(retryDelay, failureCount, error) const shouldRetry = retry === true || (typeof retry === 'number' && failureCount < retry) || diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 8c2b50a089..972758223a 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -1,16 +1,14 @@ +import type { Mutation } from './mutation' +import type { FetchOptions, Query } from './query' import type { DefaultError, - Enabled, FetchStatus, MutationKey, MutationStatus, QueryFunction, QueryKey, QueryOptions, - StaleTime, } from './types' -import type { Mutation } from './mutation' -import type { FetchOptions, Query } from './query' // TYPES @@ -79,6 +77,94 @@ export function noop(): void export function noop(): undefined export function noop() {} +/** + * Type guard that checks if a value is the function variant of a union type. + * + * This utility is designed for the common pattern in TanStack Query where options + * can be either a direct value or a function that computes that value. + * + * @template T - The direct value type + * @template TArgs - Array of argument types that the function variant accepts + * @param value - The value to check, which can be either T or a function that returns something + * @returns True if the value is a function, false otherwise. When true, TypeScript narrows the type to the function variant. + * + * @example + * ```ts + * // Basic usage with no arguments + * const initialData: string | (() => string) = getValue() + * if (isFunctionVariant(initialData)) { + * // TypeScript knows initialData is () => string here + * const result = initialData() + * } + * ``` + * + * @example + * ```ts + * // Usage with function arguments + * const staleTime: number | ((query: Query) => number) = getStaleTime() + * if (isFunctionVariant(staleTime)) { + * // TypeScript knows staleTime is (query: Query) => number here + * const result = staleTime(query) + * } + * ``` + */ +function isFunctionVariant = []>( + value: T | ((...args: TArgs) => any), +): value is (...args: TArgs) => any { + return typeof value === 'function' +} + +/** + * Resolves a value that can either be a direct value or a function that computes the value. + * + * This utility eliminates the need for repetitive `typeof value === 'function'` checks + * throughout the codebase and provides a clean way to handle the common pattern where + * options can be static values or dynamic functions. + * + * @template T - The type of the resolved value + * @template TArgs - Array of argument types when resolving function variants + * @param value - Either a direct value of type T or a function that returns T + * @param args - Arguments to pass to the function if value is a function + * @returns The resolved value of type T + * + * @example + * ```ts + * // Zero-argument function resolution (like initialData) + * const initialData: string | (() => string) = 'hello' + * const resolved = resolveValueOrFunction(initialData) // 'hello' + * + * const initialDataFn: string | (() => string) = () => 'world' + * const resolved2 = resolveValueOrFunction(initialDataFn) // 'world' + * ``` + * + * @example + * ```ts + * // Function with arguments (like staleTime, retryDelay) + * const staleTime: number | ((query: Query) => number) = (query) => query.state.dataUpdatedAt + 5000 + * const resolved = resolveValueOrFunction(staleTime, query) // number + * + * const retryDelay: number | ((failureCount: number, error: Error) => number) = 1000 + * const resolved2 = resolveValueOrFunction(retryDelay, 3, new Error()) // 1000 + * ``` + * + * @example + * ```ts + * // Replaces verbose patterns like: + * // const delay = typeof retryDelay === 'function' + * // ? retryDelay(failureCount, error) + * // : retryDelay + * + * // With: + * const delay = resolveValueOrFunction(retryDelay, failureCount, error) + * ``` + */ +export function resolveValueOrFunction>( + value: T | ((...args: TArgs) => T), + ...args: TArgs +): T { + return isFunctionVariant(value) ? value(...args) : value +} + export function functionalUpdate( updater: Updater, input: TInput, @@ -96,30 +182,6 @@ export function timeUntilStale(updatedAt: number, staleTime?: number): number { return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0) } -export function resolveStaleTime< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - staleTime: undefined | StaleTime, - query: Query, -): number | undefined { - return typeof staleTime === 'function' ? staleTime(query) : staleTime -} - -export function resolveEnabled< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - enabled: undefined | Enabled, - query: Query, -): boolean | undefined { - return typeof enabled === 'function' ? enabled(query) : enabled -} - export function matchQuery( filters: QueryFilters, query: Query, From cd59cc4ed823e495129cc494f26f8f06d0c1aa2c Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Thu, 29 May 2025 09:31:31 -0700 Subject: [PATCH 02/21] refactor(types): simplify placeholderData type definition by removing NonFunctionGuard This simplification is possible due to the introduction of generic runtime utilities that handle value-or-function resolution. The new `resolveValueOrFunction()` utility handles the distinction between direct values and functions at runtime with proper type safety, eliminating the need for complex type-level guards. --- packages/query-core/src/queryObserver.ts | 1 - packages/query-core/src/types.ts | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 4011ddf13b..4381eafe20 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -18,7 +18,6 @@ import type { PendingThenable, Thenable } from './thenable' import type { DefaultError, DefaultedQueryObserverOptions, - PlaceholderDataFunction, QueryKey, QueryObserverBaseResult, QueryObserverOptions, diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 735b8ea263..cb593e0de8 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -162,9 +162,7 @@ export type QueryFunctionContext< export type InitialDataFunction = () => T | undefined -type NonFunctionGuard = T extends Function ? never : T - -export type PlaceholderDataFunction< +type PlaceholderDataFunction< TQueryFnData = unknown, TError = DefaultError, TQueryData = TQueryFnData, @@ -420,13 +418,8 @@ export interface QueryObserverOptions< * If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided. */ placeholderData?: - | NonFunctionGuard - | PlaceholderDataFunction< - NonFunctionGuard, - TError, - NonFunctionGuard, - TQueryKey - > + | TQueryData + | PlaceholderDataFunction _optimisticResults?: 'optimistic' | 'isRestoring' From c03881af6c5015288ed66c19a3325fbc09cf1229 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Thu, 29 May 2025 10:01:53 -0700 Subject: [PATCH 03/21] chore(query): replace resolveStaleTime with resolveValueOrFunction --- packages/query-core/src/query.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index aa5feea940..9d222e93d3 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -3,8 +3,6 @@ import { noop, replaceData, resolveValueOrFunction, - resolveEnabled, - resolveStaleTime, skipToken, timeUntilStale, } from './utils' @@ -277,7 +275,7 @@ export class Query< if (this.getObserversCount() > 0) { return this.observers.some( (observer) => - resolveStaleTime(observer.options.staleTime, this) === 'static', + resolveValueOrFunction(observer.options.staleTime, this) === 'static', ) } From 44416bfe38f60b172e2448fcce86c05adbd78c27 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Thu, 29 May 2025 10:02:26 -0700 Subject: [PATCH 04/21] chore(deps): add tsup dependency and fix build --- .../package.json | 3 +- .../angular-query-experimental/package.json | 3 +- pnpm-lock.yaml | 55 +++++-------------- 3 files changed, 17 insertions(+), 44 deletions(-) diff --git a/packages/angular-query-devtools-experimental/package.json b/packages/angular-query-devtools-experimental/package.json index a60e7e8e53..30ae17fdc7 100644 --- a/packages/angular-query-devtools-experimental/package.json +++ b/packages/angular-query-devtools-experimental/package.json @@ -58,7 +58,8 @@ "@angular/platform-browser-dynamic": "^20.0.0", "@tanstack/angular-query-experimental": "workspace:*", "eslint-plugin-jsdoc": "^50.5.0", - "npm-run-all2": "^5.0.0" + "npm-run-all2": "^5.0.0", + "tsup": "^8.4.0" }, "peerDependencies": { "@angular/common": ">=16.0.0", diff --git a/packages/angular-query-experimental/package.json b/packages/angular-query-experimental/package.json index 0f882ebcac..988be50d99 100644 --- a/packages/angular-query-experimental/package.json +++ b/packages/angular-query-experimental/package.json @@ -77,7 +77,8 @@ "@angular/platform-browser-dynamic": "^20.0.0", "@tanstack/query-test-utils": "workspace:*", "eslint-plugin-jsdoc": "^50.5.0", - "npm-run-all2": "^5.0.0" + "npm-run-all2": "^5.0.0", + "tsup": "^8.4.0" }, "peerDependencies": { "@angular/common": ">=16.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff70b200c4..65dcc0d111 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1231,7 +1231,7 @@ importers: version: 6.1.18(react-native@0.76.3(@babel/core@7.26.10)(@babel/preset-env@7.27.2(@babel/core@7.26.10))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@19.0.0))(react@19.0.0) '@react-navigation/stack': specifier: ^6.4.1 - version: 6.4.1(cgoxobftzx7q2rxvco2fw2mfyq) + version: 6.4.1(1f29909f8de70b3aa674884b982ac755) '@tanstack/react-query': specifier: workspace:* version: link:../../../packages/react-query @@ -2280,7 +2280,7 @@ importers: dependencies: '@angular/common': specifier: '>=16.0.0' - version: 19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@tanstack/query-devtools': specifier: workspace:* version: link:../query-devtools @@ -2290,7 +2290,7 @@ importers: version: 20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/platform-browser-dynamic': specifier: ^20.0.0 - version: 20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@20.0.0)(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.0.0(@angular/animations@20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))) + version: 20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@20.0.0)(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.0.0(@angular/animations@20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))) '@tanstack/angular-query-experimental': specifier: workspace:* version: link:../angular-query-experimental @@ -2300,6 +2300,9 @@ importers: npm-run-all2: specifier: ^5.0.0 version: 5.0.2 + tsup: + specifier: ^8.4.0 + version: 8.4.0(@microsoft/api-extractor@7.48.1(@types/node@22.15.3))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.6.1) packages/angular-query-experimental: dependencies: @@ -2334,6 +2337,9 @@ importers: npm-run-all2: specifier: ^5.0.0 version: 5.0.2 + tsup: + specifier: ^8.4.0 + version: 8.4.0(@microsoft/api-extractor@7.48.1(@types/node@22.15.3))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.6.1) packages/angular-query-persist-client: dependencies: @@ -2367,7 +2373,7 @@ importers: version: link:../query-test-utils '@testing-library/angular': specifier: ^17.3.7 - version: 17.3.7(5fchvpeatqbl65tde2d3342iqq) + version: 17.3.7(44e06ac0fb247039d3919c909462d711) '@testing-library/dom': specifier: ^10.4.0 version: 10.4.0 @@ -2986,13 +2992,6 @@ packages: engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@19.2.4': - resolution: {integrity: sha512-5iBerI1hkY8rAt0gZQgOlfzR69jj5j25JyfkDOhdZhezE0pqhDc69OnbkUM20LTau4bFRYOj015eiKWzE2DOzQ==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} - peerDependencies: - '@angular/core': 19.2.4 - rxjs: ^6.5.3 || ^7.4.0 - '@angular/common@20.0.0': resolution: {integrity: sha512-tZTvxDjx+wH74/hIpip63u4tlaXNVXkq1iVf4gk7RPQGCAYLNPDWma8X+RpXMXWikn4/mA5NS1VBBtStTbS+gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -7802,6 +7801,7 @@ packages: '@vercel/edge@1.2.1': resolution: {integrity: sha512-1++yncEyIAi68D3UEOlytYb1IUcIulMWdoSzX2h9LuSeeyR7JtaIgR8DcTQ6+DmYOQn+5MCh6LY+UmK6QBByNA==} + deprecated: This package is deprecated. You should to use `@vercel/functions` instead. '@vercel/nft@0.29.2': resolution: {integrity: sha512-A/Si4mrTkQqJ6EXJKv5EYCDQ3NL6nJXxG8VGXePsaiQigsomHYQC9xSpX8qGk7AEZk4b1ssbYIqJ0ISQQ7bfcA==} @@ -16473,13 +16473,6 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular/animations@20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))': - dependencies: - '@angular/common': 19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0) - tslib: 2.8.1 - optional: true - '@angular/animations@20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: '@angular/common': 20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) @@ -16563,12 +16556,6 @@ snapshots: - chokidar - supports-color - '@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': - dependencies: - '@angular/core': 20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0) - rxjs: 7.8.2 - tslib: 2.8.1 - '@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: '@angular/core': 20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0) @@ -16611,14 +16598,6 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@angular/platform-browser-dynamic@20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@20.0.0)(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.0.0(@angular/animations@20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))': - dependencies: - '@angular/common': 19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 20.0.0 - '@angular/core': 20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 20.0.0(@angular/animations@20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)) - tslib: 2.8.1 - '@angular/platform-browser-dynamic@20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@20.0.0)(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.0.0(@angular/animations@20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: '@angular/common': 20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) @@ -16627,14 +16606,6 @@ snapshots: '@angular/platform-browser': 20.0.0(@angular/animations@20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@20.0.0(@angular/animations@20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))': - dependencies: - '@angular/common': 19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0) - tslib: 2.8.1 - optionalDependencies: - '@angular/animations': 20.0.0(@angular/common@19.2.4(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/platform-browser@20.0.0(@angular/animations@20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: '@angular/common': 20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) @@ -20997,7 +20968,7 @@ snapshots: dependencies: nanoid: 3.3.8 - '@react-navigation/stack@6.4.1(cgoxobftzx7q2rxvco2fw2mfyq)': + '@react-navigation/stack@6.4.1(1f29909f8de70b3aa674884b982ac755)': dependencies: '@react-navigation/elements': 1.3.31(@react-navigation/native@6.1.18(react-native@0.76.3(@babel/core@7.26.10)(@babel/preset-env@7.27.2(@babel/core@7.26.10))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@19.0.0))(react@19.0.0))(react-native-safe-area-context@4.12.0(react-native@0.76.3(@babel/core@7.26.10)(@babel/preset-env@7.27.2(@babel/core@7.26.10))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@19.0.0))(react@19.0.0))(react-native@0.76.3(@babel/core@7.26.10)(@babel/preset-env@7.27.2(@babel/core@7.26.10))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@19.0.0))(react@19.0.0) '@react-navigation/native': 6.1.18(react-native@0.76.3(@babel/core@7.26.10)(@babel/preset-env@7.27.2(@babel/core@7.26.10))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@19.0.0))(react@19.0.0) @@ -21786,7 +21757,7 @@ snapshots: - tsx - yaml - '@testing-library/angular@17.3.7(5fchvpeatqbl65tde2d3342iqq)': + '@testing-library/angular@17.3.7(44e06ac0fb247039d3919c909462d711)': dependencies: '@angular/animations': 20.0.0(@angular/common@20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': 20.0.0(@angular/core@20.0.0(@angular/compiler@20.0.0)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) From f940937986044e62b74542f9fb0a39c934090d0a Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Mon, 11 Aug 2025 00:56:34 -0700 Subject: [PATCH 05/21] refactor: rename resolveValueOrFunction to resolveOption for consistency --- packages/query-core/src/query.ts | 10 +++--- packages/query-core/src/queryClient.ts | 4 +-- packages/query-core/src/queryObserver.ts | 40 ++++++++++++------------ packages/query-core/src/retryer.ts | 4 +-- packages/query-core/src/utils.ts | 12 +++---- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 029f4ad736..5cb8ad9395 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -2,7 +2,7 @@ import { ensureQueryFn, noop, replaceData, - resolveValueOrFunction, + resolveOption, skipToken, timeUntilStale, } from './utils' @@ -255,7 +255,7 @@ export class Query< isActive(): boolean { return this.observers.some( (observer) => - resolveValueOrFunction(observer.options.enabled, this) !== false, + resolveOption(observer.options.enabled, this) !== false, ) } @@ -274,7 +274,7 @@ export class Query< if (this.getObserversCount() > 0) { return this.observers.some( (observer) => - resolveValueOrFunction(observer.options.staleTime, this) === 'static', + resolveOption(observer.options.staleTime, this) === 'static', ) } @@ -690,12 +690,12 @@ function getDefaultState< >( options: QueryOptions, ): QueryState { - const data = resolveValueOrFunction(options.initialData) + const data = resolveOption(options.initialData) const hasData = data !== undefined const initialDataUpdatedAt = hasData - ? resolveValueOrFunction(options.initialDataUpdatedAt) + ? resolveOption(options.initialDataUpdatedAt) : 0 return { diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index a2d2dafb3b..ddb911c84f 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -4,7 +4,7 @@ import { hashQueryKeyByOptions, noop, partialMatchKey, - resolveValueOrFunction, + resolveOption, skipToken, } from './utils' import { QueryCache } from './queryCache' @@ -156,7 +156,7 @@ export class QueryClient { if ( options.revalidateIfStale && query.isStaleByTime( - resolveValueOrFunction(defaultedOptions.staleTime, query), + resolveOption(defaultedOptions.staleTime, query), ) ) { void this.prefetchQuery(defaultedOptions) diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index df665d6115..1e7d3fc09c 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -8,7 +8,7 @@ import { isValidTimeout, noop, replaceData, - resolveValueOrFunction, + resolveOption, shallowEqualObjects, timeUntilStale, } from './utils' @@ -155,7 +155,7 @@ export class QueryObserver< this.options.enabled !== undefined && typeof this.options.enabled !== 'boolean' && typeof this.options.enabled !== 'function' && - typeof resolveValueOrFunction( + typeof resolveOption( this.options.enabled, this.#currentQuery, ) !== 'boolean' @@ -201,10 +201,10 @@ export class QueryObserver< if ( mounted && (this.#currentQuery !== prevQuery || - resolveValueOrFunction(this.options.enabled, this.#currentQuery) !== - resolveValueOrFunction(prevOptions.enabled, this.#currentQuery) || - resolveValueOrFunction(this.options.staleTime, this.#currentQuery) !== - resolveValueOrFunction(prevOptions.staleTime, this.#currentQuery)) + resolveOption(this.options.enabled, this.#currentQuery) !== + resolveOption(prevOptions.enabled, this.#currentQuery) || + resolveOption(this.options.staleTime, this.#currentQuery) !== + resolveOption(prevOptions.staleTime, this.#currentQuery)) ) { this.#updateStaleTimeout() } @@ -215,8 +215,8 @@ export class QueryObserver< if ( mounted && (this.#currentQuery !== prevQuery || - resolveValueOrFunction(this.options.enabled, this.#currentQuery) !== - resolveValueOrFunction(prevOptions.enabled, this.#currentQuery) || + resolveOption(this.options.enabled, this.#currentQuery) !== + resolveOption(prevOptions.enabled, this.#currentQuery) || nextRefetchInterval !== this.#currentRefetchInterval) ) { this.#updateRefetchInterval(nextRefetchInterval) @@ -344,7 +344,7 @@ export class QueryObserver< #updateStaleTimeout(): void { this.#clearStaleTimeout() - const staleTime = resolveValueOrFunction( + const staleTime = resolveOption( this.options.staleTime, this.#currentQuery, ) @@ -368,7 +368,7 @@ export class QueryObserver< #computeRefetchInterval() { return ( - resolveValueOrFunction( + resolveOption( this.options.refetchInterval, this.#currentQuery, ) ?? false @@ -382,7 +382,7 @@ export class QueryObserver< if ( isServer || - resolveValueOrFunction(this.options.enabled, this.#currentQuery) === + resolveOption(this.options.enabled, this.#currentQuery) === false || !isValidTimeout(this.#currentRefetchInterval) || this.#currentRefetchInterval === 0 @@ -491,7 +491,7 @@ export class QueryObserver< skipSelect = true } else { // compute placeholderData - placeholderData = resolveValueOrFunction( + placeholderData = resolveOption( options.placeholderData, this.#lastQueryWithDefinedData?.state.data, this.#lastQueryWithDefinedData as any, @@ -659,7 +659,7 @@ export class QueryObserver< const { notifyOnChangeProps } = this.options const notifyOnChangePropsValue = - resolveValueOrFunction(notifyOnChangeProps) + resolveOption(notifyOnChangeProps) if ( notifyOnChangePropsValue === 'all' || @@ -737,7 +737,7 @@ function shouldLoadOnMount( options: QueryObserverOptions, ): boolean { return ( - resolveValueOrFunction(options.enabled, query) !== false && + resolveOption(options.enabled, query) !== false && query.state.data === undefined && !(query.state.status === 'error' && options.retryOnMount === false) ) @@ -762,10 +762,10 @@ function shouldFetchOn( (typeof options)['refetchOnReconnect'], ) { if ( - resolveValueOrFunction(options.enabled, query) !== false && - resolveValueOrFunction(options.staleTime, query) !== 'static' + resolveOption(options.enabled, query) !== false && + resolveOption(options.staleTime, query) !== 'static' ) { - const value = resolveValueOrFunction(field, query) + const value = resolveOption(field, query) return value === 'always' || (value !== false && isStale(query, options)) } @@ -780,7 +780,7 @@ function shouldFetchOptionally( ): boolean { return ( (query !== prevQuery || - resolveValueOrFunction(prevOptions.enabled, query) === false) && + resolveOption(prevOptions.enabled, query) === false) && (!options.suspense || query.state.status !== 'error') && isStale(query, options) ) @@ -791,8 +791,8 @@ function isStale( options: QueryObserverOptions, ): boolean { return ( - resolveValueOrFunction(options.enabled, query) !== false && - query.isStaleByTime(resolveValueOrFunction(options.staleTime, query)) + resolveOption(options.enabled, query) !== false && + query.isStaleByTime(resolveOption(options.staleTime, query)) ) } diff --git a/packages/query-core/src/retryer.ts b/packages/query-core/src/retryer.ts index b8573445cc..0d58eaf249 100644 --- a/packages/query-core/src/retryer.ts +++ b/packages/query-core/src/retryer.ts @@ -1,7 +1,7 @@ import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { pendingThenable } from './thenable' -import { isServer, resolveValueOrFunction, sleep } from './utils' +import { isServer, resolveOption, sleep } from './utils' import type { CancelOptions, DefaultError, NetworkMode } from './types' // TYPES @@ -166,7 +166,7 @@ export function createRetryer( // Do we need to retry the request? const retry = config.retry ?? (isServer ? 0 : 3) const retryDelay = config.retryDelay ?? defaultRetryDelay - const delay = resolveValueOrFunction(retryDelay, failureCount, error) + const delay = resolveOption(retryDelay, failureCount, error) const shouldRetry = retry === true || (typeof retry === 'number' && failureCount < retry) || diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index a9e66c0d12..9ae163cae0 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -131,20 +131,20 @@ function isFunctionVariant = []>( * ```ts * // Zero-argument function resolution (like initialData) * const initialData: string | (() => string) = 'hello' - * const resolved = resolveValueOrFunction(initialData) // 'hello' + * const resolved = resolveOption(initialData) // 'hello' * * const initialDataFn: string | (() => string) = () => 'world' - * const resolved2 = resolveValueOrFunction(initialDataFn) // 'world' + * const resolved2 = resolveOption(initialDataFn) // 'world' * ``` * * @example * ```ts * // Function with arguments (like staleTime, retryDelay) * const staleTime: number | ((query: Query) => number) = (query) => query.state.dataUpdatedAt + 5000 - * const resolved = resolveValueOrFunction(staleTime, query) // number + * const resolved = resolveOption(staleTime, query) // number * * const retryDelay: number | ((failureCount: number, error: Error) => number) = 1000 - * const resolved2 = resolveValueOrFunction(retryDelay, 3, new Error()) // 1000 + * const resolved2 = resolveOption(retryDelay, 3, new Error()) // 1000 * ``` * * @example @@ -155,10 +155,10 @@ function isFunctionVariant = []>( * // : retryDelay * * // With: - * const delay = resolveValueOrFunction(retryDelay, failureCount, error) + * const delay = resolveOption(retryDelay, failureCount, error) * ``` */ -export function resolveValueOrFunction>( +export function resolveOption>( value: T | ((...args: TArgs) => T), ...args: TArgs ): T { From a69316643cbe4942ae2a5679229aec74eec04e7c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 07:57:34 +0000 Subject: [PATCH 06/21] ci: apply automated fixes --- packages/query-core/src/query.ts | 3 +-- packages/query-core/src/queryClient.ts | 4 +--- packages/query-core/src/queryObserver.ts | 22 ++++++---------------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 5cb8ad9395..6d3fb2fdfd 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -254,8 +254,7 @@ export class Query< isActive(): boolean { return this.observers.some( - (observer) => - resolveOption(observer.options.enabled, this) !== false, + (observer) => resolveOption(observer.options.enabled, this) !== false, ) } diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index ddb911c84f..6b8197f158 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -155,9 +155,7 @@ export class QueryClient { if ( options.revalidateIfStale && - query.isStaleByTime( - resolveOption(defaultedOptions.staleTime, query), - ) + query.isStaleByTime(resolveOption(defaultedOptions.staleTime, query)) ) { void this.prefetchQuery(defaultedOptions) } diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 1e7d3fc09c..585c04c72d 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -155,10 +155,8 @@ export class QueryObserver< this.options.enabled !== undefined && typeof this.options.enabled !== 'boolean' && typeof this.options.enabled !== 'function' && - typeof resolveOption( - this.options.enabled, - this.#currentQuery, - ) !== 'boolean' + typeof resolveOption(this.options.enabled, this.#currentQuery) !== + 'boolean' ) { throw new Error( 'Expected enabled to be a boolean or a callback that returns a boolean', @@ -344,10 +342,7 @@ export class QueryObserver< #updateStaleTimeout(): void { this.#clearStaleTimeout() - const staleTime = resolveOption( - this.options.staleTime, - this.#currentQuery, - ) + const staleTime = resolveOption(this.options.staleTime, this.#currentQuery) if (isServer || this.#currentResult.isStale || !isValidTimeout(staleTime)) { return @@ -368,10 +363,7 @@ export class QueryObserver< #computeRefetchInterval() { return ( - resolveOption( - this.options.refetchInterval, - this.#currentQuery, - ) ?? false + resolveOption(this.options.refetchInterval, this.#currentQuery) ?? false ) } @@ -382,8 +374,7 @@ export class QueryObserver< if ( isServer || - resolveOption(this.options.enabled, this.#currentQuery) === - false || + resolveOption(this.options.enabled, this.#currentQuery) === false || !isValidTimeout(this.#currentRefetchInterval) || this.#currentRefetchInterval === 0 ) { @@ -658,8 +649,7 @@ export class QueryObserver< } const { notifyOnChangeProps } = this.options - const notifyOnChangePropsValue = - resolveOption(notifyOnChangeProps) + const notifyOnChangePropsValue = resolveOption(notifyOnChangeProps) if ( notifyOnChangePropsValue === 'all' || From 24d06b5b44273c1ed093aa3a6574edc94354203a Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Mon, 11 Aug 2025 00:59:04 -0700 Subject: [PATCH 07/21] refactor(queryClient): update method call to use resolveOption for improved clarity --- packages/query-core/src/queryClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 6b8197f158..7528c1df05 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -363,7 +363,7 @@ export class QueryClient { const query = this.#queryCache.build(this, defaultedOptions) return query.isStaleByTime( - resolveValueOrFunction(defaultedOptions.staleTime, query), + resolveOption(defaultedOptions.staleTime, query), ) ? query.fetch(defaultedOptions) : Promise.resolve(query.state.data as TData) From 568d2d090faa5f92d5426baafd29aea6678cbd67 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Mon, 11 Aug 2025 08:35:11 -0700 Subject: [PATCH 08/21] refactor: replace resolveEnabled with resolveOption for consistency Remove specialized resolveEnabled function and consolidate functionality into the generic resolveOption utility. Both functions performed identical operations but resolveEnabled was type-specific to query enabled state. - Remove resolveEnabled function from utils.ts - Update queryObserver.ts to use resolveOption instead - Remove unused Enabled type import - Maintain identical functionality and type safety --- packages/query-core/src/queryObserver.ts | 2 +- packages/query-core/src/utils.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 585c04c72d..ea64abaa33 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -565,7 +565,7 @@ export class QueryObserver< isStale: isStale(query, options), refetch: this.refetch, promise: this.#currentThenable, - isEnabled: resolveEnabled(options.enabled, query) !== false, + isEnabled: resolveOption(options.enabled, query) !== false, } const nextResult = result as QueryObserverResult diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 9ae163cae0..7e47cc7c64 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -165,6 +165,7 @@ export function resolveOption>( return isFunctionVariant(value) ? value(...args) : value } + export function functionalUpdate( updater: Updater, input: TInput, From df4850a767c148351a22c2e8c11e83d19cfd7f9f Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:42:59 +0000 Subject: [PATCH 09/21] ci: apply automated fixes --- packages/query-core/src/queryClient.ts | 4 +--- packages/query-core/src/utils.ts | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 7528c1df05..420e71fb9f 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -362,9 +362,7 @@ export class QueryClient { const query = this.#queryCache.build(this, defaultedOptions) - return query.isStaleByTime( - resolveOption(defaultedOptions.staleTime, query), - ) + return query.isStaleByTime(resolveOption(defaultedOptions.staleTime, query)) ? query.fetch(defaultedOptions) : Promise.resolve(query.state.data as TData) } diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 7e47cc7c64..9ae163cae0 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -165,7 +165,6 @@ export function resolveOption>( return isFunctionVariant(value) ? value(...args) : value } - export function functionalUpdate( updater: Updater, input: TInput, From d6ea2f19230f7f4bbeca10aed273f2be099a95f4 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:50:20 -0700 Subject: [PATCH 10/21] chore: remove unused dependency tsup from package.json --- packages/angular-query-devtools-experimental/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/angular-query-devtools-experimental/package.json b/packages/angular-query-devtools-experimental/package.json index 607ada7c58..c605b0f136 100644 --- a/packages/angular-query-devtools-experimental/package.json +++ b/packages/angular-query-devtools-experimental/package.json @@ -58,8 +58,7 @@ "@angular/platform-browser": "^20.0.0", "@tanstack/angular-query-experimental": "workspace:*", "eslint-plugin-jsdoc": "^50.5.0", - "npm-run-all2": "^5.0.0", - "tsup": "^8.4.0" + "npm-run-all2": "^5.0.0" }, "peerDependencies": { "@angular/common": ">=16.0.0", From 6fff3432f59fe2427b2580aded726e34624a1698 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:09:04 -0700 Subject: [PATCH 11/21] refactor: simplify functionalUpdate by using resolveOption --- packages/query-core/src/utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 9ae163cae0..3efe9ee63e 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -169,9 +169,7 @@ export function functionalUpdate( updater: Updater, input: TInput, ): TOutput { - return typeof updater === 'function' - ? (updater as (_: TInput) => TOutput)(input) - : updater + return resolveOption(updater, input) } export function isValidTimeout(value: unknown): value is number { From 8854b1ac3f07b8e015e649f76427156706235e01 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:09:03 -0700 Subject: [PATCH 12/21] fix: correct spelling of 'retryer' in comment --- packages/query-core/src/query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index 9b9d42fe2f..22062c2436 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -376,7 +376,7 @@ export class Query< ): Promise { if ( this.state.fetchStatus !== 'idle' && - // If the promise in the retyer is already rejected, we have to definitely + // If the promise in the retryer is already rejected, we have to definitely // re-start the fetch; there is a chance that the query is still in a // pending state when that happens this.#retryer?.status() !== 'rejected' From 77bab2755ab10bcc831a2a3a4a8fd9f35c16b40e Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:37:17 -0700 Subject: [PATCH 13/21] chore: pnpm lock --- pnpm-lock.yaml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8c33349f3..12e5e74a78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2180,7 +2180,7 @@ importers: version: 5.6.3(webpack@5.98.0) webpack: specifier: ^5.96.1 - version: 5.98.0(webpack-cli@5.1.4) + version: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) webpack-cli: specifier: ^5.1.4 version: 5.1.4(webpack@5.98.0) @@ -2267,9 +2267,6 @@ importers: npm-run-all2: specifier: ^5.0.0 version: 5.0.2 - tsup: - specifier: ^8.4.0 - version: 8.4.0(@microsoft/api-extractor@7.48.1(@types/node@22.15.3))(jiti@2.4.2)(postcss@8.5.6)(tsx@4.20.1)(typescript@5.8.3)(yaml@2.8.0) packages/angular-query-experimental: dependencies: @@ -20931,7 +20928,7 @@ snapshots: '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.98.0)': dependencies: - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.98.0) '@webpack-cli/info@1.5.0(webpack-cli@4.10.0)': @@ -20941,7 +20938,7 @@ snapshots: '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.98.0)': dependencies: - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.98.0) '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0)': @@ -20950,7 +20947,7 @@ snapshots: '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.98.0)': dependencies: - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.98.0) '@xmldom/xmldom@0.7.13': {} @@ -21436,7 +21433,7 @@ snapshots: '@babel/core': 7.27.1 find-cache-dir: 4.0.0 schema-utils: 4.3.0 - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) babel-plugin-istanbul@6.1.1: dependencies: @@ -24381,7 +24378,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.1 optionalDependencies: - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) htmlparser2@10.0.0: dependencies: @@ -29286,14 +29283,16 @@ snapshots: webpack-sources: 1.4.3 worker-farm: 1.7.0 - terser-webpack-plugin@5.3.11(webpack@5.98.0): + terser-webpack-plugin@5.3.11(esbuild@0.25.5)(webpack@5.98.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 4.3.0 serialize-javascript: 6.0.2 terser: 5.39.1 - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) + optionalDependencies: + esbuild: 0.25.5 terser@4.8.1: dependencies: @@ -30490,7 +30489,7 @@ snapshots: import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.98.0(webpack-cli@5.1.4) + webpack: 5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4) webpack-merge: 5.10.0 webpack-merge@5.10.0: @@ -30538,7 +30537,7 @@ snapshots: transitivePeerDependencies: - supports-color - webpack@5.98.0(webpack-cli@5.1.4): + webpack@5.98.0(esbuild@0.25.5)(webpack-cli@5.1.4): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.7 @@ -30560,7 +30559,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.11(webpack@5.98.0) + terser-webpack-plugin: 5.3.11(esbuild@0.25.5)(webpack@5.98.0) watchpack: 2.4.2 webpack-sources: 3.2.3 optionalDependencies: From 8955b0d02da070e097adafd9e2f62b7d90f2cdd6 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:42:39 -0700 Subject: [PATCH 14/21] refactor(core): constrain resolveOption to non-function types Add NonFunction type constraint to resolveOption's generic parameter T to prevent ambiguity when T itself is a function type. This ensures the value-or-function pattern remains unambiguous by restricting T to primitives, objects, and other non-function types. - Define NonFunction union type covering all non-function types - Apply constraint to resolveOption - Update functionalUpdate to use same constraint - Remove isFunctionVariant helper (inlined typeof check) - Simplify resolveOption implementation This prevents TypeScript errors where function types could be ambiguous in the T | (() => T) pattern, making the API more type-safe. --- packages/query-core/src/utils.ts | 56 +++++++++----------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 3efe9ee63e..c03704206e 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -77,43 +77,6 @@ export function noop(): void export function noop(): undefined export function noop() {} -/** - * Type guard that checks if a value is the function variant of a union type. - * - * This utility is designed for the common pattern in TanStack Query where options - * can be either a direct value or a function that computes that value. - * - * @template T - The direct value type - * @template TArgs - Array of argument types that the function variant accepts - * @param value - The value to check, which can be either T or a function that returns something - * @returns True if the value is a function, false otherwise. When true, TypeScript narrows the type to the function variant. - * - * @example - * ```ts - * // Basic usage with no arguments - * const initialData: string | (() => string) = getValue() - * if (isFunctionVariant(initialData)) { - * // TypeScript knows initialData is () => string here - * const result = initialData() - * } - * ``` - * - * @example - * ```ts - * // Usage with function arguments - * const staleTime: number | ((query: Query) => number) = getStaleTime() - * if (isFunctionVariant(staleTime)) { - * // TypeScript knows staleTime is (query: Query) => number here - * const result = staleTime(query) - * } - * ``` - */ -function isFunctionVariant = []>( - value: T | ((...args: TArgs) => any), -): value is (...args: TArgs) => any { - return typeof value === 'function' -} - /** * Resolves a value that can either be a direct value or a function that computes the value. * @@ -158,14 +121,27 @@ function isFunctionVariant = []>( * const delay = resolveOption(retryDelay, failureCount, error) * ``` */ -export function resolveOption>( +type NonFunction = + | string + | number + | boolean + | bigint + | symbol + | null + | undefined + | object + +export function resolveOption< + T extends NonFunction, + TArgs extends Array +>( value: T | ((...args: TArgs) => T), ...args: TArgs ): T { - return isFunctionVariant(value) ? value(...args) : value + return typeof value === 'function' ? value(...args) : value } -export function functionalUpdate( +export function functionalUpdate( updater: Updater, input: TInput, ): TOutput { From 92a0b66f20a9c73476ae54339dee3168cdc34a7b Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:16:04 -0700 Subject: [PATCH 15/21] docs(core): improve NonFunction type organization and documentation Move NonFunction type definition above resolveOption function for better logical flow and add comprehensive JSDoc documentation explaining its purpose in preventing recursive type ambiguity in value-or-function patterns. - Add JSDoc for NonFunction type explaining constraint rationale - Move type definition before its usage for better code organization - Update resolveOption JSDoc to clarify T is constrained to non-function types - Improve code readability and developer understanding of type constraints --- packages/query-core/src/utils.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 1ba263c648..f82424291c 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -78,6 +78,22 @@ export function noop(): void export function noop(): undefined export function noop() {} +/** + * Constraint type that excludes function types to prevent ambiguity in value-or-function patterns. + * + * This ensures that T in resolveOption cannot be a function type itself, which would create + * recursive ambiguity about whether to call the function or return it as the resolved value. + */ +type NonFunction = + | string + | number + | boolean + | bigint + | symbol + | null + | undefined + | object + /** * Resolves a value that can either be a direct value or a function that computes the value. * @@ -85,7 +101,7 @@ export function noop() {} * throughout the codebase and provides a clean way to handle the common pattern where * options can be static values or dynamic functions. * - * @template T - The type of the resolved value + * @template T - The type of the resolved value (constrained to non-function types) * @template TArgs - Array of argument types when resolving function variants * @param value - Either a direct value of type T or a function that returns T * @param args - Arguments to pass to the function if value is a function @@ -122,16 +138,6 @@ export function noop() {} * const delay = resolveOption(retryDelay, failureCount, error) * ``` */ -type NonFunction = - | string - | number - | boolean - | bigint - | symbol - | null - | undefined - | object - export function resolveOption< T extends NonFunction, TArgs extends Array From aaaaf3c1db04499bd94900127e7bc50f9b02eee5 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:26:08 -0700 Subject: [PATCH 16/21] refactor(core): improve resolveOption function with better type safety - Move NonFunction to generic utility type that excludes function types - Update Updater type to use NonFunction for better type constraints - Add function overloads to resolveOption for optional vs required values - Improve implementation with explicit type assertions and clearer logic - Remove functionalUpdate function (replaced by resolveOption usage) - Enhance documentation with better examples and type explanations --- packages/query-core/src/utils.ts | 57 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index f82424291c..1dd5feec7e 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -66,7 +66,14 @@ export interface MutationFilters< status?: MutationStatus } -export type Updater = TOutput | ((input: TInput) => TOutput) +/** + * Utility type that excludes function types from T. + * If T is a function, it resolves to `never`, effectively removing T + * from unions and preventing ambiguity in value-or-function patterns. + */ +export type NonFunction = T extends (...args: Array) => any ? never : T + +export type Updater = NonFunction | ((input: TInput) => TOutput) export type QueryTypeFilter = 'all' | 'active' | 'inactive' @@ -78,21 +85,6 @@ export function noop(): void export function noop(): undefined export function noop() {} -/** - * Constraint type that excludes function types to prevent ambiguity in value-or-function patterns. - * - * This ensures that T in resolveOption cannot be a function type itself, which would create - * recursive ambiguity about whether to call the function or return it as the resolved value. - */ -type NonFunction = - | string - | number - | boolean - | bigint - | symbol - | null - | undefined - | object /** * Resolves a value that can either be a direct value or a function that computes the value. @@ -138,22 +130,29 @@ type NonFunction = * const delay = resolveOption(retryDelay, failureCount, error) * ``` */ -export function resolveOption< - T extends NonFunction, - TArgs extends Array ->( - value: T | ((...args: TArgs) => T), +export function resolveOption>( + valueOrFn: NonFunction | ((...args: TArgs) => T) | undefined, + ...args: TArgs +): T | undefined +// Overload for when value is guaranteed to be present +export function resolveOption>( + valueOrFn: NonFunction | ((...args: TArgs) => T), + ...args: TArgs +): T +// Implementation +export function resolveOption>( + valueOrFn: NonFunction | ((...args: TArgs) => T) | undefined, ...args: TArgs -): T { - return typeof value === 'function' ? value(...args) : value +): T | undefined { + if (typeof valueOrFn === 'function') { + // Because of our NonFunction utility, TypeScript now correctly + // infers that if valueOrFn is a function, it must be the producer `(...args: TArgs) => T`. + return (valueOrFn as (...args: TArgs) => T)(...args) + } + // If it's not a function, it must be of type T or undefined. + return valueOrFn as T | undefined } -export function functionalUpdate( - updater: Updater, - input: TInput, -): TOutput { - return resolveOption(updater, input) -} export function isValidTimeout(value: unknown): value is number { return typeof value === 'number' && value >= 0 && value !== Infinity From 87398fdb98b2620d892a847bc9367e4f4948b0e4 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:27:36 -0700 Subject: [PATCH 17/21] refactor(query-core): replace functionalUpdate with resolveOption in QueryClient Replace functionalUpdate usage with resolveOption for consistency with the new unified value-or-function resolution pattern. --- packages/query-core/src/queryClient.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index f9cc326616..ac41c9ceca 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -1,5 +1,4 @@ import { - functionalUpdate, hashKey, hashQueryKeyByOptions, noop, @@ -197,7 +196,7 @@ export class QueryClient { defaultedOptions.queryHash, ) const prevData = query?.state.data - const data = functionalUpdate(updater, prevData) + const data = resolveOption(updater, prevData) if (data === undefined) { return undefined From 251bf4a999a40979a2fabe17c20ef27d8ef8f7d1 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:57:22 -0700 Subject: [PATCH 18/21] docs(core): enhance resolveOption function documentation Add detailed explanations of NonFunction constraint benefits, overload behavior for optional vs required values, and improved type safety guarantees. --- packages/query-core/src/utils.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 1dd5feec7e..c86d9b1bdf 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -93,6 +93,14 @@ export function noop() {} * throughout the codebase and provides a clean way to handle the common pattern where * options can be static values or dynamic functions. * + * The NonFunction constraint eliminates ambiguity by ensuring T can never be a function + * type. This makes the value-or-function pattern type-safe and unambiguous. + * + * The function provides two overloads: one that includes `| undefined` for optional values + * (where the value might not be provided), and another without `| undefined` for required + * values. This allows proper type inference for both optional config parameters and + * required ones while maintaining type safety. + * * @template T - The type of the resolved value (constrained to non-function types) * @template TArgs - Array of argument types when resolving function variants * @param value - Either a direct value of type T or a function that returns T From ecd18dbfe7298339e7e2caee6f5caf3bdbad55d9 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 00:12:35 +0000 Subject: [PATCH 19/21] ci: apply automated fixes --- packages/query-core/src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index c86d9b1bdf..4a16f06aa3 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -73,7 +73,9 @@ export interface MutationFilters< */ export type NonFunction = T extends (...args: Array) => any ? never : T -export type Updater = NonFunction | ((input: TInput) => TOutput) +export type Updater = + | NonFunction + | ((input: TInput) => TOutput) export type QueryTypeFilter = 'all' | 'active' | 'inactive' @@ -85,7 +87,6 @@ export function noop(): void export function noop(): undefined export function noop() {} - /** * Resolves a value that can either be a direct value or a function that computes the value. * @@ -161,7 +162,6 @@ export function resolveOption>( return valueOrFn as T | undefined } - export function isValidTimeout(value: unknown): value is number { return typeof value === 'number' && value >= 0 && value !== Infinity } From a8c2ddcae1f6861937c20fb5a6596f1a2bf75943 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Sat, 4 Oct 2025 11:18:25 -0700 Subject: [PATCH 20/21] refactor(query-core): consolidate NonFunction type into NonFunctionGuard Moved NonFunction type from utils.ts to types.ts as NonFunctionGuard with improved documentation. Applied the type guard consistently to placeholderData and updated all references and documentation to use the new name. --- packages/query-core/src/types.ts | 17 +++++++++++++++-- packages/query-core/src/utils.ts | 20 +++++++------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index f5ad5015c6..d27abebf9d 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -166,6 +166,14 @@ export type QueryFunctionContext< export type InitialDataFunction = () => T | undefined +/** + * `NonFunctionGuard` ensures T is not a function type. + * + * If T is a function, it resolves to `never`, effectively removing T + * from unions and preventing ambiguity in value-or-function patterns. + */ +export type NonFunctionGuard = T extends Function ? never : T + type PlaceholderDataFunction< TQueryFnData = unknown, TError = DefaultError, @@ -422,8 +430,13 @@ export interface QueryObserverOptions< * If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided. */ placeholderData?: - | TQueryData - | PlaceholderDataFunction + | NonFunctionGuard + | PlaceholderDataFunction< + NonFunctionGuard, + TError, + NonFunctionGuard, + TQueryKey + > _optimisticResults?: 'optimistic' | 'isRestoring' diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 4a16f06aa3..607b228cac 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -6,6 +6,7 @@ import type { FetchStatus, MutationKey, MutationStatus, + NonFunctionGuard, QueryFunction, QueryKey, QueryOptions, @@ -66,15 +67,8 @@ export interface MutationFilters< status?: MutationStatus } -/** - * Utility type that excludes function types from T. - * If T is a function, it resolves to `never`, effectively removing T - * from unions and preventing ambiguity in value-or-function patterns. - */ -export type NonFunction = T extends (...args: Array) => any ? never : T - export type Updater = - | NonFunction + | NonFunctionGuard | ((input: TInput) => TOutput) export type QueryTypeFilter = 'all' | 'active' | 'inactive' @@ -94,7 +88,7 @@ export function noop() {} * throughout the codebase and provides a clean way to handle the common pattern where * options can be static values or dynamic functions. * - * The NonFunction constraint eliminates ambiguity by ensuring T can never be a function + * The NonFunctionGuard constraint eliminates ambiguity by ensuring T can never be a function * type. This makes the value-or-function pattern type-safe and unambiguous. * * The function provides two overloads: one that includes `| undefined` for optional values @@ -140,21 +134,21 @@ export function noop() {} * ``` */ export function resolveOption>( - valueOrFn: NonFunction | ((...args: TArgs) => T) | undefined, + valueOrFn: NonFunctionGuard | ((...args: TArgs) => T) | undefined, ...args: TArgs ): T | undefined // Overload for when value is guaranteed to be present export function resolveOption>( - valueOrFn: NonFunction | ((...args: TArgs) => T), + valueOrFn: NonFunctionGuard | ((...args: TArgs) => T), ...args: TArgs ): T // Implementation export function resolveOption>( - valueOrFn: NonFunction | ((...args: TArgs) => T) | undefined, + valueOrFn: NonFunctionGuard | ((...args: TArgs) => T) | undefined, ...args: TArgs ): T | undefined { if (typeof valueOrFn === 'function') { - // Because of our NonFunction utility, TypeScript now correctly + // Because of our NonFunctionGuard utility, TypeScript now correctly // infers that if valueOrFn is a function, it must be the producer `(...args: TArgs) => T`. return (valueOrFn as (...args: TArgs) => T)(...args) } From 85954600e8f95df28447c4b70978a29f160ba19c Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Sat, 4 Oct 2025 17:27:49 -0700 Subject: [PATCH 21/21] fix(types): export PlaceholderDataFunction type for better accessibility --- packages/query-core/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index d27abebf9d..9f52fbd2c4 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -174,7 +174,7 @@ export type InitialDataFunction = () => T | undefined */ export type NonFunctionGuard = T extends Function ? never : T -type PlaceholderDataFunction< +export type PlaceholderDataFunction< TQueryFnData = unknown, TError = DefaultError, TQueryData = TQueryFnData,