Skip to content

Commit

Permalink
fix(vue-query): useQueries type inference (#7416)
Browse files Browse the repository at this point in the history
  • Loading branch information
DamianOsipiuk authored Jun 26, 2024
1 parent bec24e7 commit 11e979e
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 24 deletions.
77 changes: 77 additions & 0 deletions packages/vue-query/src/__tests__/useQueries.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,81 @@ describe('UseQueries config object overload', () => {
expectTypeOf(queriesState[0].data).toEqualTypeOf<string | undefined>()
})
})

// Fix #7270
it('should have proper type inference with different options provided', () => {
const numbers = [1, 2, 3]
const queryKey = (n: number) => [n]
const queryFn = (n: number) => () => Promise.resolve(n)
const select = (data: number) => data.toString()

const queries = numbers.map((n) => ({
queryKey: [n],
queryFn: () => Promise.resolve(n),
select: (data: number) => data.toString(),
}))

const queriesWithoutSelect = numbers.map((n) => ({
queryKey: queryKey(n),
queryFn: queryFn(n),
}))

const queriesWithQueryOptions = numbers.map((n) =>
queryOptions({
queryKey: queryKey(n),
queryFn: queryFn(n),
select,
}),
)

const queriesWithQueryOptionsWithoutSelect = numbers.map((n) =>
queryOptions({
queryKey: queryKey(n),
queryFn: queryFn(n),
}),
)

const query1 = useQueries({ queries: queries })
expectTypeOf(query1.value).toEqualTypeOf<
Array<QueryObserverResult<string, Error>>
>()

const query2 = useQueries({ queries: queriesWithoutSelect })
expectTypeOf(query2.value).toEqualTypeOf<
Array<QueryObserverResult<number, Error>>
>()

const query3 = useQueries({ queries: queriesWithQueryOptions })
expectTypeOf(query3.value).toEqualTypeOf<
Array<QueryObserverResult<string, Error>>
>()

const query4 = useQueries({ queries: queriesWithQueryOptionsWithoutSelect })
expectTypeOf(query4.value).toEqualTypeOf<
Array<QueryObserverResult<number, Error>>
>()

const queryCombine = useQueries({
queries: queries,
combine: (data) => {
return data.reduce((acc, i) => {
acc.push(i.data ?? '')
return acc
}, [] as Array<string>)
},
})
expectTypeOf(queryCombine.value).toEqualTypeOf<Array<string>>()

const queryCombineWithoutSelect = useQueries({
queries: queriesWithoutSelect,
combine: (data) => {
return data.reduce((acc, i) => {
acc.push(i.data ?? 0)
return acc
}, [] as Array<number>)
},
})

expectTypeOf(queryCombineWithoutSelect.value).toEqualTypeOf<Array<number>>()
})
})
65 changes: 41 additions & 24 deletions packages/vue-query/src/useQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ import type {
DefaultError,
DefinedQueryObserverResult,
QueriesObserverOptions,
QueriesPlaceholderDataFunction,
QueryFunction,
QueryKey,
QueryObserverResult,
ThrowOnError,
} from '@tanstack/query-core'
import type { UseQueryOptions } from './useQuery'
import type { QueryClient } from './queryClient'
import type { DeepUnwrapRef, DistributiveOmit, MaybeRefDeep } from './types'
import type { DeepUnwrapRef, MaybeRefDeep } from './types'

// This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.
// `placeholderData` function does not have a parameter
Expand All @@ -32,20 +31,15 @@ type UseQueryOptionsForUseQueries<
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = DistributiveOmit<
UseQueryOptions<TQueryFnData, TError, TData, unknown, TQueryKey>,
'placeholderData'
> & {
placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction<TQueryFnData>
}
> = UseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>

// Avoid TS depth-limit error in case of large array literal
type MAXIMUM_DEPTH = 20

// Widen the type of the symbol to enable type inference even if skipToken is not immutable.
type SkipTokenForUseQueries = symbol

type GetOptions<T> =
type GetUseQueryOptionsForUseQueries<T> =
// Part 1: if UseQueryOptions are already being sent through, then just return T
T extends UseQueryOptions
? DeepUnwrapRef<T>
Expand Down Expand Up @@ -81,8 +75,20 @@ type GetOptions<T> =
unknown extends TData ? TQueryFnData : TData,
TQueryKey
>
: // Fallback
UseQueryOptionsForUseQueries
: T extends {
queryFn?:
| QueryFunction<infer TQueryFnData, infer TQueryKey>
| SkipTokenForUseQueries
throwOnError?: ThrowOnError<any, infer TError, any, any>
}
? UseQueryOptionsForUseQueries<
TQueryFnData,
TError,
TQueryFnData,
TQueryKey
>
: // Fallback
UseQueryOptionsForUseQueries

// A defined initialData setting should return a DefinedQueryObserverResult rather than QueryObserverResult
type GetDefinedOrUndefinedQueryResult<T, TData, TError = unknown> = T extends {
Expand All @@ -101,7 +107,7 @@ type GetDefinedOrUndefinedQueryResult<T, TData, TError = unknown> = T extends {
: QueryObserverResult<TData, TError>
: QueryObserverResult<TData, TError>

type GetResults<T> =
type GetUseQueryResult<T> =
// Part 1: if using UseQueryOptions then the types are already set
T extends UseQueryOptions<
infer TQueryFnData,
Expand Down Expand Up @@ -142,26 +148,37 @@ type GetResults<T> =
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: // Fallback
QueryObserverResult
: T extends {
queryFn?:
| QueryFunction<infer TQueryFnData, any>
| SkipTokenForUseQueries
throwOnError?: ThrowOnError<any, infer TError, any, any>
}
? GetDefinedOrUndefinedQueryResult<
T,
TQueryFnData,
unknown extends TError ? DefaultError : TError
>
: // Fallback
QueryObserverResult

/**
* UseQueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
*/
export type UseQueriesOptions<
T extends Array<any>,
TResult extends Array<any> = [],
TResults extends Array<any> = [],
TDepth extends ReadonlyArray<number> = [],
> = TDepth['length'] extends MAXIMUM_DEPTH
? Array<UseQueryOptionsForUseQueries>
: T extends []
? []
: T extends [infer Head]
? [...TResult, GetOptions<Head>]
: T extends [infer Head, ...infer Tail]
? [...TResults, GetUseQueryOptionsForUseQueries<Head>]
: T extends [infer Head, ...infer Tails]
? UseQueriesOptions<
[...Tail],
[...TResult, GetOptions<Head>],
[...Tails],
[...TResults, GetUseQueryOptionsForUseQueries<Head>],
[...TDepth, 1]
>
: ReadonlyArray<unknown> extends T
Expand Down Expand Up @@ -192,18 +209,18 @@ export type UseQueriesOptions<
*/
export type UseQueriesResults<
T extends Array<any>,
TResult extends Array<any> = [],
TResults extends Array<any> = [],
TDepth extends ReadonlyArray<number> = [],
> = TDepth['length'] extends MAXIMUM_DEPTH
? Array<QueryObserverResult>
: T extends []
? []
: T extends [infer Head]
? [...TResult, GetResults<Head>]
: T extends [infer Head, ...infer Tail]
? [...TResults, GetUseQueryResult<Head>]
: T extends [infer Head, ...infer Tails]
? UseQueriesResults<
[...Tail],
[...TResult, GetResults<Head>],
[...Tails],
[...TResults, GetUseQueryResult<Head>],
[...TDepth, 1]
>
: T extends Array<
Expand Down

0 comments on commit 11e979e

Please sign in to comment.