Skip to content

Commit 11f4ea9

Browse files
fix(useQueries): allow types to work with strongly typed QueryFunction + QueryKey (TanStack#3214)
1 parent 26b3954 commit 11f4ea9

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

src/react/tests/useQueries.test.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
QueryCache,
1919
QueryObserverResult,
2020
QueriesObserver,
21+
QueryFunction,
2122
} from '../..'
2223

2324
describe('useQueries', () => {
@@ -899,6 +900,62 @@ describe('useQueries', () => {
899900
}
900901
})
901902

903+
it('handles types for QueryFunction factory with strongly typed QueryKey', () => {
904+
type QueryKeyA = ['queryA']
905+
const getQueryKeyA = (): QueryKeyA => ['queryA']
906+
type GetQueryFunctionA = () => QueryFunction<number, QueryKeyA>
907+
const getQueryFunctionA: GetQueryFunctionA = () => async () => {
908+
return 1
909+
}
910+
type SelectorA = (data: number) => [number, string]
911+
const getSelectorA = (): SelectorA => data => [data, data.toString()]
912+
913+
type QueryKeyB = ['queryB', string]
914+
const getQueryKeyB = (id: string): QueryKeyB => ['queryB', id]
915+
type GetQueryFunctionB = () => QueryFunction<string, QueryKeyB>
916+
const getQueryFunctionB: GetQueryFunctionB = () => async () => {
917+
return '1'
918+
}
919+
type SelectorB = (data: string) => [string, number]
920+
const getSelectorB = (): SelectorB => data => [data, +data]
921+
922+
// @ts-expect-error (Page component is not rendered)
923+
// eslint-disable-next-line
924+
function Page() {
925+
const result = useQueries([
926+
{
927+
queryKey: getQueryKeyA(),
928+
queryFn: getQueryFunctionA(),
929+
},
930+
{
931+
queryKey: getQueryKeyB('id'),
932+
queryFn: getQueryFunctionB(),
933+
},
934+
])
935+
expectType<QueryObserverResult<number, unknown>>(result[0])
936+
expectType<QueryObserverResult<string, unknown>>(result[1])
937+
938+
const withSelector = useQueries([
939+
{
940+
queryKey: getQueryKeyA(),
941+
queryFn: getQueryFunctionA(),
942+
select: getSelectorA(),
943+
},
944+
{
945+
queryKey: getQueryKeyB('id'),
946+
queryFn: getQueryFunctionB(),
947+
select: getSelectorB(),
948+
},
949+
])
950+
expectType<QueryObserverResult<[number, string], unknown>>(
951+
withSelector[0]
952+
)
953+
expectType<QueryObserverResult<[string, number], unknown>>(
954+
withSelector[1]
955+
)
956+
}
957+
})
958+
902959
it('should not change state if unmounted', async () => {
903960
const key1 = queryKey()
904961

src/react/useQueries.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ type GetOptions<T extends any> =
3030
? UseQueryOptions<TQueryFnData>
3131
: // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
3232
T extends {
33-
queryFn?: QueryFunction<infer TQueryFnData>
33+
queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey>
3434
select: (data: any) => infer TData
3535
}
36-
? UseQueryOptions<TQueryFnData, unknown, TData>
37-
: T extends { queryFn?: QueryFunction<infer TQueryFnData> }
38-
? UseQueryOptions<TQueryFnData>
36+
? UseQueryOptions<TQueryFnData, unknown, TData, TQueryKey>
37+
: T extends { queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey> }
38+
? UseQueryOptions<TQueryFnData, unknown, TQueryFnData, TQueryKey>
3939
: // Fallback
4040
UseQueryOptions
4141

@@ -56,11 +56,11 @@ type GetResults<T> =
5656
? UseQueryResult<TQueryFnData>
5757
: // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
5858
T extends {
59-
queryFn?: QueryFunction<any>
59+
queryFn?: QueryFunction<any, any>
6060
select: (data: any) => infer TData
6161
}
6262
? UseQueryResult<TData>
63-
: T extends { queryFn?: QueryFunction<infer TQueryFnData> }
63+
: T extends { queryFn?: QueryFunction<infer TQueryFnData, any> }
6464
? UseQueryResult<TQueryFnData>
6565
: // Fallback
6666
UseQueryResult

0 commit comments

Comments
 (0)