Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
11 changes: 11 additions & 0 deletions .changeset/fix-vue-query-options-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@tanstack/vue-query': patch
---

fix(vue-query): fix queryOptions return type to expose all properties

The `queryOptions` return type now correctly exposes all query option
properties (like `queryFn`, `staleTime`, etc.) for direct access, not
just `queryKey` and `initialData`. This is achieved by excluding the
`Ref` and `ComputedRef` branches from the `MaybeRef` union in the return
type, since `queryOptions` always returns a plain object.
10 changes: 10 additions & 0 deletions packages/vue-query/src/__tests__/queryOptions.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,14 @@ describe('queryOptions', () => {

expectTypeOf(data).toEqualTypeOf<number>()
})

it('should allow accessing queryFn and other properties on the returned options object', () => {
const options = queryOptions({
queryKey: ['groups'],
queryFn: () => Promise.resolve([]),
})

expectTypeOf(options.queryFn).not.toBeUndefined()
expectTypeOf(options.queryKey).not.toBeUndefined()
})
})
16 changes: 14 additions & 2 deletions packages/vue-query/src/queryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ import type {
DefinedInitialQueryOptions,
UndefinedInitialQueryOptions,
} from './useQuery'
import type { ComputedRef, Ref } from 'vue-demi'

// Removes Ref and ComputedRef branches from a MaybeRef union type,
// leaving only the plain object type so all properties are directly
// accessible via dot notation.
// Fixes #7892: queryOptions return type only contains queryKey and
// initialData properties due to MaybeRef union narrowing.
type PlainQueryOptions<T> = Exclude<T, Ref<any> | ComputedRef<any>>

export function queryOptions<
TQueryFnData = unknown,
Expand All @@ -11,7 +19,9 @@ export function queryOptions<
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
): DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
): PlainQueryOptions<
DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>
> & {
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
}

Expand All @@ -22,7 +32,9 @@ export function queryOptions<
TQueryKey extends QueryKey = QueryKey,
>(
options: UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
): UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
): PlainQueryOptions<
UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>
> & {
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
}

Expand Down
18 changes: 18 additions & 0 deletions packages/vue-query/src/useQueries.ts
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can you add some tests for the things you changed in useQueries as well please? Something that fails on main but is fixed in your PR 🙏

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

just pushed two type tests for the useQueries changes - one for plain queryOptions (no initialData) and one with select. both exercise the DataTag inference path that was broken on main. let me know if you want me to add runtime tests too or if these cover it!

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useQueryClient } from './useQueryClient'
import { cloneDeepUnref } from './utils'
import type { Ref } from 'vue-demi'
import type {
DataTag,
DefaultError,
DefinedQueryObserverResult,
QueriesObserverOptions,
Expand Down Expand Up @@ -45,6 +46,10 @@ type GetUseQueryOptionsForUseQueries<T> =
// Part 1: if UseQueryOptions are already being sent through, then just return T
T extends UseQueryOptions
? DeepUnwrapRef<T>
: // Part 1b: handle plain query options returned by queryOptions()
// (ExtractPlainType removes MaybeRef branches, so they need DeepUnwrapRef too)
T extends { queryKey: DataTag<any, any, any> }
? DeepUnwrapRef<T>
: // Part 2: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
T extends {
queryFnData: infer TQueryFnData
Expand Down Expand Up @@ -123,6 +128,19 @@ type GetUseQueryResult<T> =
undefined extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: // Part 1b: handle plain query options from queryOptions()
T extends { queryKey: DataTag<any, infer TQueryFnData, infer TError> }
? T extends { select?: (data: any) => infer TData }
? GetDefinedOrUndefinedQueryResult<
T,
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: GetDefinedOrUndefinedQueryResult<
T,
TQueryFnData,
unknown extends TError ? DefaultError : TError
>
: // Part 2: responsible for mapping explicit type parameter to function result, if object
T extends { queryFnData: any; error?: infer TError; data: infer TData }
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
Expand Down
Loading