From 6988ff413d3db606cf989ccba866690ffaf6d0c9 Mon Sep 17 00:00:00 2001 From: Damian Osipiuk Date: Sun, 12 May 2024 01:53:02 +0200 Subject: [PATCH] fix(vue-query): useQueries type inference --- .../src/__tests__/useQueries.test-d.ts | 77 +++++++++++++++++++ packages/vue-query/src/useQueries.ts | 65 ++++++++++------ 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/packages/vue-query/src/__tests__/useQueries.test-d.ts b/packages/vue-query/src/__tests__/useQueries.test-d.ts index 3ac8001415..dcf2be0383 100644 --- a/packages/vue-query/src/__tests__/useQueries.test-d.ts +++ b/packages/vue-query/src/__tests__/useQueries.test-d.ts @@ -142,4 +142,81 @@ describe('UseQueries config object overload', () => { expectTypeOf(queriesState[0].data).toEqualTypeOf() }) }) + + // 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> + >() + + const query2 = useQueries({ queries: queriesWithoutSelect }) + expectTypeOf(query2.value).toEqualTypeOf< + Array> + >() + + const query3 = useQueries({ queries: queriesWithQueryOptions }) + expectTypeOf(query3.value).toEqualTypeOf< + Array> + >() + + const query4 = useQueries({ queries: queriesWithQueryOptionsWithoutSelect }) + expectTypeOf(query4.value).toEqualTypeOf< + Array> + >() + + const queryCombine = useQueries({ + queries: queries, + combine: (data) => { + return data.reduce((acc, i) => { + acc.push(i.data ?? '') + return acc + }, [] as Array) + }, + }) + expectTypeOf(queryCombine.value).toEqualTypeOf>() + + const queryCombineWithoutSelect = useQueries({ + queries: queriesWithoutSelect, + combine: (data) => { + return data.reduce((acc, i) => { + acc.push(i.data ?? 0) + return acc + }, [] as Array) + }, + }) + + expectTypeOf(queryCombineWithoutSelect.value).toEqualTypeOf>() + }) }) diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index 3ff8d422fd..b67d8f9b4f 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -15,7 +15,6 @@ import type { DefaultError, DefinedQueryObserverResult, QueriesObserverOptions, - QueriesPlaceholderDataFunction, QueryFunction, QueryKey, QueryObserverResult, @@ -23,7 +22,7 @@ import type { } 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 @@ -32,12 +31,7 @@ type UseQueryOptionsForUseQueries< TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = DistributiveOmit< - UseQueryOptions, - 'placeholderData' -> & { - placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction -} +> = UseQueryOptions // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 @@ -45,7 +39,7 @@ 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 = +type GetUseQueryOptionsForUseQueries = // Part 1: if UseQueryOptions are already being sent through, then just return T T extends UseQueryOptions ? DeepUnwrapRef @@ -81,8 +75,20 @@ type GetOptions = unknown extends TData ? TQueryFnData : TData, TQueryKey > - : // Fallback - UseQueryOptionsForUseQueries + : T extends { + queryFn?: + | QueryFunction + | SkipTokenForUseQueries + throwOnError?: ThrowOnError + } + ? UseQueryOptionsForUseQueries< + TQueryFnData, + TError, + TQueryFnData, + TQueryKey + > + : // Fallback + UseQueryOptionsForUseQueries // A defined initialData setting should return a DefinedQueryObserverResult rather than QueryObserverResult type GetDefinedOrUndefinedQueryResult = T extends { @@ -101,7 +107,7 @@ type GetDefinedOrUndefinedQueryResult = T extends { : QueryObserverResult : QueryObserverResult -type GetResults = +type GetUseQueryResult = // Part 1: if using UseQueryOptions then the types are already set T extends UseQueryOptions< infer TQueryFnData, @@ -142,26 +148,37 @@ type GetResults = unknown extends TData ? TQueryFnData : TData, unknown extends TError ? DefaultError : TError > - : // Fallback - QueryObserverResult + : T extends { + queryFn?: + | QueryFunction + | SkipTokenForUseQueries + throwOnError?: ThrowOnError + } + ? 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, - TResult extends Array = [], + TResults extends Array = [], TDepth extends ReadonlyArray = [], > = TDepth['length'] extends MAXIMUM_DEPTH ? Array : T extends [] ? [] : T extends [infer Head] - ? [...TResult, GetOptions] - : T extends [infer Head, ...infer Tail] + ? [...TResults, GetUseQueryOptionsForUseQueries] + : T extends [infer Head, ...infer Tails] ? UseQueriesOptions< - [...Tail], - [...TResult, GetOptions], + [...Tails], + [...TResults, GetUseQueryOptionsForUseQueries], [...TDepth, 1] > : ReadonlyArray extends T @@ -192,18 +209,18 @@ export type UseQueriesOptions< */ export type UseQueriesResults< T extends Array, - TResult extends Array = [], + TResults extends Array = [], TDepth extends ReadonlyArray = [], > = TDepth['length'] extends MAXIMUM_DEPTH ? Array : T extends [] ? [] : T extends [infer Head] - ? [...TResult, GetResults] - : T extends [infer Head, ...infer Tail] + ? [...TResults, GetUseQueryResult] + : T extends [infer Head, ...infer Tails] ? UseQueriesResults< - [...Tail], - [...TResult, GetResults], + [...Tails], + [...TResults, GetUseQueryResult], [...TDepth, 1] > : T extends Array<