Skip to content

Commit 8955b0d

Browse files
committed
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<T extends NonFunction, ...> - 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.
1 parent ae7a70b commit 8955b0d

File tree

1 file changed

+16
-40
lines changed

1 file changed

+16
-40
lines changed

packages/query-core/src/utils.ts

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -77,43 +77,6 @@ export function noop(): void
7777
export function noop(): undefined
7878
export function noop() {}
7979

80-
/**
81-
* Type guard that checks if a value is the function variant of a union type.
82-
*
83-
* This utility is designed for the common pattern in TanStack Query where options
84-
* can be either a direct value or a function that computes that value.
85-
*
86-
* @template T - The direct value type
87-
* @template TArgs - Array of argument types that the function variant accepts
88-
* @param value - The value to check, which can be either T or a function that returns something
89-
* @returns True if the value is a function, false otherwise. When true, TypeScript narrows the type to the function variant.
90-
*
91-
* @example
92-
* ```ts
93-
* // Basic usage with no arguments
94-
* const initialData: string | (() => string) = getValue()
95-
* if (isFunctionVariant(initialData)) {
96-
* // TypeScript knows initialData is () => string here
97-
* const result = initialData()
98-
* }
99-
* ```
100-
*
101-
* @example
102-
* ```ts
103-
* // Usage with function arguments
104-
* const staleTime: number | ((query: Query) => number) = getStaleTime()
105-
* if (isFunctionVariant<number, [Query]>(staleTime)) {
106-
* // TypeScript knows staleTime is (query: Query) => number here
107-
* const result = staleTime(query)
108-
* }
109-
* ```
110-
*/
111-
function isFunctionVariant<T, TArgs extends Array<any> = []>(
112-
value: T | ((...args: TArgs) => any),
113-
): value is (...args: TArgs) => any {
114-
return typeof value === 'function'
115-
}
116-
11780
/**
11881
* Resolves a value that can either be a direct value or a function that computes the value.
11982
*
@@ -158,14 +121,27 @@ function isFunctionVariant<T, TArgs extends Array<any> = []>(
158121
* const delay = resolveOption(retryDelay, failureCount, error)
159122
* ```
160123
*/
161-
export function resolveOption<T, TArgs extends Array<any>>(
124+
type NonFunction =
125+
| string
126+
| number
127+
| boolean
128+
| bigint
129+
| symbol
130+
| null
131+
| undefined
132+
| object
133+
134+
export function resolveOption<
135+
T extends NonFunction,
136+
TArgs extends Array<any>
137+
>(
162138
value: T | ((...args: TArgs) => T),
163139
...args: TArgs
164140
): T {
165-
return isFunctionVariant(value) ? value(...args) : value
141+
return typeof value === 'function' ? value(...args) : value
166142
}
167143

168-
export function functionalUpdate<TInput, TOutput>(
144+
export function functionalUpdate<TInput, TOutput extends NonFunction>(
169145
updater: Updater<TInput, TOutput>,
170146
input: TInput,
171147
): TOutput {

0 commit comments

Comments
 (0)