Skip to content

Commit aaaaf3c

Browse files
committed
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<TOutput> 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
1 parent 92a0b66 commit aaaaf3c

File tree

1 file changed

+28
-29
lines changed

1 file changed

+28
-29
lines changed

packages/query-core/src/utils.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@ export interface MutationFilters<
6666
status?: MutationStatus
6767
}
6868

69-
export type Updater<TInput, TOutput> = TOutput | ((input: TInput) => TOutput)
69+
/**
70+
* Utility type that excludes function types from T.
71+
* If T is a function, it resolves to `never`, effectively removing T
72+
* from unions and preventing ambiguity in value-or-function patterns.
73+
*/
74+
export type NonFunction<T> = T extends (...args: Array<any>) => any ? never : T
75+
76+
export type Updater<TInput, TOutput> = NonFunction<TOutput> | ((input: TInput) => TOutput)
7077

7178
export type QueryTypeFilter = 'all' | 'active' | 'inactive'
7279

@@ -78,21 +85,6 @@ export function noop(): void
7885
export function noop(): undefined
7986
export function noop() {}
8087

81-
/**
82-
* Constraint type that excludes function types to prevent ambiguity in value-or-function patterns.
83-
*
84-
* This ensures that T in resolveOption<T> cannot be a function type itself, which would create
85-
* recursive ambiguity about whether to call the function or return it as the resolved value.
86-
*/
87-
type NonFunction =
88-
| string
89-
| number
90-
| boolean
91-
| bigint
92-
| symbol
93-
| null
94-
| undefined
95-
| object
9688

9789
/**
9890
* Resolves a value that can either be a direct value or a function that computes the value.
@@ -138,22 +130,29 @@ type NonFunction =
138130
* const delay = resolveOption(retryDelay, failureCount, error)
139131
* ```
140132
*/
141-
export function resolveOption<
142-
T extends NonFunction,
143-
TArgs extends Array<any>
144-
>(
145-
value: T | ((...args: TArgs) => T),
133+
export function resolveOption<T, TArgs extends Array<any>>(
134+
valueOrFn: NonFunction<T> | ((...args: TArgs) => T) | undefined,
135+
...args: TArgs
136+
): T | undefined
137+
// Overload for when value is guaranteed to be present
138+
export function resolveOption<T, TArgs extends Array<any>>(
139+
valueOrFn: NonFunction<T> | ((...args: TArgs) => T),
140+
...args: TArgs
141+
): T
142+
// Implementation
143+
export function resolveOption<T, TArgs extends Array<any>>(
144+
valueOrFn: NonFunction<T> | ((...args: TArgs) => T) | undefined,
146145
...args: TArgs
147-
): T {
148-
return typeof value === 'function' ? value(...args) : value
146+
): T | undefined {
147+
if (typeof valueOrFn === 'function') {
148+
// Because of our NonFunction<T> utility, TypeScript now correctly
149+
// infers that if valueOrFn is a function, it must be the producer `(...args: TArgs) => T`.
150+
return (valueOrFn as (...args: TArgs) => T)(...args)
151+
}
152+
// If it's not a function, it must be of type T or undefined.
153+
return valueOrFn as T | undefined
149154
}
150155

151-
export function functionalUpdate<TInput, TOutput extends NonFunction>(
152-
updater: Updater<TInput, TOutput>,
153-
input: TInput,
154-
): TOutput {
155-
return resolveOption(updater, input)
156-
}
157156

158157
export function isValidTimeout(value: unknown): value is number {
159158
return typeof value === 'number' && value >= 0 && value !== Infinity

0 commit comments

Comments
 (0)