Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions packages/query-core/src/__tests__/queryClient.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ describe('setQueryData', () => {

expectTypeOf(data).toEqualTypeOf<string | undefined>()
})

it('should preserve updater parameter type inference when used in functions with explicit return types', () => {
const queryKey = ['key'] as DataTag<Array<string>, number>
const queryClient = new QueryClient()

// Simulate usage inside a function with explicit return type
// The outer function returns 'unknown' but this shouldn't affect the updater's type inference
;(() =>
queryClient.setQueryData(queryKey, (data) => {
expectTypeOf(data).toEqualTypeOf<number | undefined>()
return data
})) satisfies () => unknown
})
})

describe('getQueryState', () => {
Expand Down Expand Up @@ -590,3 +603,28 @@ describe('resetQueries', () => {
})
})
})
type SuccessCallback = () => unknown
it('should infer types correctly with expression body arrow functions', () => {
const queryKey = ['key'] as DataTag<Array<string>, number>
const queryClient = new QueryClient()

// @ts-expect-error

Choose a reason for hiding this comment

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

medium

With the NoInfer fix on setQueryData, this assignment should be type-correct, and the type of data within the updater is correctly inferred. This means there should be no type error on the following line.

The @ts-expect-error directive is likely now causing an "Unused @ts-expect-error" warning from the TypeScript compiler and should be removed.

const callbackTest: SuccessCallback = () =>
queryClient.setQueryData(queryKey, (data) => {
expectTypeOf(data).toEqualTypeOf<number | undefined>()
return data
})
})

it('should infer types correctly with block body arrow functions', () => {
const queryKey = ['key'] as DataTag<Array<string>, number>
const queryClient = new QueryClient()

// @ts-expect-error

Choose a reason for hiding this comment

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

medium

Similar to the test above, this @ts-expect-error directive should be removed. The fix makes the assignment valid, so a type error is not expected.

const callbackTest2: SuccessCallback = () => {
queryClient.setQueryData(queryKey, (data) => {
expectTypeOf(data).toEqualTypeOf<number | undefined>()
return data
})
}
})
2 changes: 1 addition & 1 deletion packages/query-core/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export class QueryClient {
NoInfer<TInferredQueryFnData> | undefined
>,
options?: SetDataOptions,
): TInferredQueryFnData | undefined {
): NoInfer<TInferredQueryFnData> | undefined {
Copy link

Choose a reason for hiding this comment

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

Suggestion: Revert the explicit NoInfer return type on setQueryData back to the original inferred return type to avoid surprising inference changes for direct-value callers; alternatively, if keeping NoInfer here, apply NoInfer consistently to the related setQueriesData return type — ensure updater inference and consumers' expectations remain stable. [possible issue]

Copy link

Choose a reason for hiding this comment

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

Type Safety Pattern Enhancement

NoInfer wrapper prevents TypeScript from widening generic types in complex inference scenarios, maintaining type safety when setQueryData is used in functions with explicit return types. This ensures consistent type behavior across different usage patterns and prevents runtime type mismatches.

Standards
  • Clean-Code-Type-Safety
  • SOLID-OCP
  • Maintainability-Quality-Consistency

const defaultedOptions = this.defaultQueryOptions<
any,
any,
Expand Down
13 changes: 13 additions & 0 deletions packages/vue-query/src/__tests__/queryClient.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,19 @@ describe('setQueryData', () => {

expectTypeOf(data).toEqualTypeOf<string | undefined>()
})

it('should preserve updater parameter type inference when used in functions with explicit return types', () => {
const queryKey = ['key'] as DataTag<Array<string>, number>
const queryClient = new QueryClient()

// Simulate usage inside a function with explicit return type
// The outer function returns 'unknown' but this shouldn't affect the updater's type inference
;(() =>
queryClient.setQueryData(queryKey, (data) => {
expectTypeOf(data).toEqualTypeOf<number | undefined>()
return data
})) satisfies () => unknown
})
})

describe('fetchInfiniteQuery', () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/vue-query/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,17 @@ export class QueryClient extends QC {
NoInfer<TInferredQueryFnData> | undefined
>,
options?: MaybeRefDeep<SetDataOptions>,
): TInferredQueryFnData | undefined
): NoInfer<TInferredQueryFnData> | undefined
Copy link

Choose a reason for hiding this comment

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

Suggestion: Add a trailing semicolon to the overload signature to make it a proper TypeScript overload declaration. [possible bug]

Suggested change
): NoInfer<TInferredQueryFnData> | undefined
): NoInfer<TInferredQueryFnData> | undefined;
Why Change? ⭐

In TypeScript, overload signatures (declarations without an implementation) must be terminated with a semicolon.
The snippet being changed is an overload signature that precedes the implementation signature (which has a body).
Adding a trailing semicolon corrects the syntax without changing any runtime behavior. It does not reference any
identifiers outside the shown context and is consistent with the surrounding overload patterns in the file.
Assumptions: the next signature in the file is the implementation (has a function body), and the TypeScript
compiler expects overload terminators. This is a purely syntactic fix and will not introduce bugs.

setQueryData<TQueryFnData, TData = NoUnknown<TQueryFnData>>(
queryKey: MaybeRefDeep<QueryKey>,
updater: Updater<NoInfer<TData> | undefined, NoInfer<TData> | undefined>,
options?: MaybeRefDeep<SetDataOptions>,
): TData | undefined
): NoInfer<TData> | undefined
Copy link

Choose a reason for hiding this comment

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

Suggestion: Add a trailing semicolon to the second overload signature so both overloads are declared consistently and validly. [possible bug]

Suggested change
): NoInfer<TData> | undefined
): NoInfer<TData> | undefined;
Why Change? ⭐

This is the second overload signature in the same overload group. As with the first suggestion,
TypeScript overload signatures must end with semicolons. Adding the semicolon here fixes syntax,
keeps overload declarations consistent, and does not alter runtime semantics. The change purely
affects declaration syntax and relies only on surrounding code already present in the file (the
implementation signature with a body follows). No new identifiers or behavior are introduced.

setQueryData<TData>(
queryKey: MaybeRefDeep<QueryKey>,
updater: Updater<TData | undefined, TData | undefined>,
options: MaybeRefDeep<SetDataOptions> = {},
): TData | undefined {
): NoInfer<TData> | undefined {
Comment on lines +117 to +127
Copy link

Choose a reason for hiding this comment

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

Vue Consistency Pattern

Vue client maintains type inference consistency with core client through NoInfer wrapper application. This prevents type widening in Vue-specific contexts while preserving generic type relationships, ensuring consistent developer experience across both implementations.

Standards
  • Clean-Code-Consistency
  • SOLID-LSP
  • Maintainability-Quality-Interface-Consistency

return super.setQueryData(
cloneDeepUnref(queryKey),
updater,
Expand Down
Loading