Skip to content

Commit

Permalink
fix(*): infer data type correctly when conditional skipToken is passe…
Browse files Browse the repository at this point in the history
…d to useQueries (#7150)

* fix(react-query): infer data type correctly when conditional skipToken is passed to useQueries

* refactor: add OmitKeyof to useQueries.test-d.tsx

* fix(*): Ensure correct data type with conditional skipToken

---------

Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
  • Loading branch information
gwansikk and TkDodo committed Mar 25, 2024
1 parent 93ce4ae commit a559c48
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 21 deletions.
10 changes: 8 additions & 2 deletions packages/angular-query-experimental/src/inject-queries.ts
Expand Up @@ -136,7 +136,7 @@ export type QueriesOptions<
[...TResult, GetOptions<Head>],
[...TDepth, 1]
>
: Array<unknown> extends T
: ReadonlyArray<unknown> extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
Expand Down Expand Up @@ -219,7 +219,13 @@ export function injectQueries<
// Make sure the results are already in fetching state before subscribing or updating options
defaultedOptions._optimisticResults = 'optimistic'

return defaultedOptions
return defaultedOptions as QueryObserverOptions<
unknown,
Error,
unknown,
unknown,
QueryKey
>
})
})

Expand Down
18 changes: 17 additions & 1 deletion packages/react-query/src/__tests__/useQueries.test-d.tsx
@@ -1,6 +1,7 @@
import { describe, expectTypeOf, it } from 'vitest'
import { queryOptions } from '../queryOptions'
import { skipToken } from '..'
import { useQueries } from '../useQueries'
import { queryOptions } from '../queryOptions'
import type { OmitKeyof } from '..'
import type { UseQueryOptions } from '../types'

Expand Down Expand Up @@ -124,4 +125,19 @@ describe('UseQueries config object overload', () => {
expectTypeOf(data).toEqualTypeOf<Data | undefined>()
})
})

it('TData should have correct type when conditional skipToken is passed', () => {
const queryResults = useQueries({
queries: [
{
queryKey: ['withSkipToken'],
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
},
],
})

const data = queryResults[0].data

expectTypeOf(data).toEqualTypeOf<number | undefined>()
})
})
13 changes: 11 additions & 2 deletions packages/react-query/src/useQueries.ts
Expand Up @@ -33,6 +33,7 @@ import type {
QueryClient,
QueryFunction,
QueryKey,
QueryObserverOptions,
SkipToken,
ThrowOnError,
} from '@tanstack/query-core'
Expand Down Expand Up @@ -178,7 +179,7 @@ export type QueriesOptions<
[...TResult, GetOptions<Head>],
[...TDepth, 1]
>
: Array<unknown> extends T
: ReadonlyArray<unknown> extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
Expand Down Expand Up @@ -258,7 +259,15 @@ export function useQueries<
const defaultedQueries = React.useMemo(
() =>
queries.map((opts) => {
const defaultedOptions = client.defaultQueryOptions(opts)
const defaultedOptions = client.defaultQueryOptions(
opts as QueryObserverOptions<
unknown,
Error,
unknown,
unknown,
QueryKey
>,
)

// Make sure the results are already in fetching state before subscribing or updating options
defaultedOptions._optimisticResults = isRestoring
Expand Down
4 changes: 2 additions & 2 deletions packages/solid-query/src/createQueries.ts
Expand Up @@ -148,7 +148,7 @@ type QueriesOptions<
[...TResult, GetOptions<Head>],
[...TDepth, 1]
>
: Array<unknown> extends T
: ReadonlyArray<unknown> extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
Expand Down Expand Up @@ -221,7 +221,7 @@ export function createQueries<
const client = createMemo(() => useQueryClient(queryClient?.()))
const isRestoring = useIsRestoring()

const defaultedQueries = createMemo(() =>
const defaultedQueries: QueriesOptions<any> = createMemo(() =>
queriesOptions().queries.map((options) =>
mergeProps(client().defaultQueryOptions(options), {
get _optimisticResults() {
Expand Down
12 changes: 10 additions & 2 deletions packages/svelte-query/src/createQueries.ts
Expand Up @@ -139,7 +139,7 @@ export type QueriesOptions<
[...TResult, GetOptions<Head>],
[...TDepth, 1]
>
: Array<unknown> extends T
: Readonly<unknown> extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
Expand Down Expand Up @@ -221,7 +221,15 @@ export function createQueries<
[queriesStore, isRestoring],
([$queries, $isRestoring]) => {
return $queries.map((opts) => {
const defaultedOptions = client.defaultQueryOptions(opts)
const defaultedOptions = client.defaultQueryOptions(
opts as QueryObserverOptions<
unknown,
Error,
unknown,
unknown,
QueryKey
>,
)
// Make sure the results are already in fetching state before subscribing or updating options
defaultedOptions._optimisticResults = $isRestoring
? 'isRestoring'
Expand Down
20 changes: 19 additions & 1 deletion packages/vue-query/src/__tests__/useQueries.types.test.ts
@@ -1,6 +1,6 @@
import { describe, it } from 'vitest'
import { reactive } from 'vue'
import { useQueries } from '..'
import { skipToken, useQueries } from '..'
import { queryOptions } from '../queryOptions'
import { doNotExecute } from './test-utils'
import type { OmitKeyof } from '..'
Expand Down Expand Up @@ -124,6 +124,24 @@ describe('UseQueries config object overload', () => {
})
})

it('TData should have correct type when conditional skipToken is passed', () => {
doNotExecute(() => {
const { value: queriesState } = useQueries({
queries: [
{
queryKey: ['key'],
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
},
],
})

const data = queriesState[0].data

const result: Expect<Equal<number | undefined, typeof data>> = true
return result
})
})

describe('custom hook', () => {
it('should allow custom hooks using UseQueryOptions', () => {
doNotExecute(() => {
Expand Down
24 changes: 13 additions & 11 deletions packages/vue-query/src/useQueries.ts
Expand Up @@ -182,7 +182,7 @@ export type UseQueriesOptions<
[...TResult, GetOptions<Head>],
[...TDepth, 1]
>
: Array<unknown> extends T
: Readonly<unknown> extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
Expand Down Expand Up @@ -270,18 +270,20 @@ export function useQueries<
const client = queryClient || useQueryClient()

const defaultedQueries = computed(() =>
cloneDeepUnref(queries).map((queryOptions) => {
if (typeof queryOptions.enabled === 'function') {
queryOptions.enabled = queryOptions.enabled()
}
cloneDeepUnref(queries as MaybeRefDeep<UseQueriesOptionsArg<any>>).map(
(queryOptions) => {
if (typeof queryOptions.enabled === 'function') {
queryOptions.enabled = queryOptions.enabled()
}

const defaulted = client.defaultQueryOptions(queryOptions)
defaulted._optimisticResults = client.isRestoring.value
? 'isRestoring'
: 'optimistic'
const defaulted = client.defaultQueryOptions(queryOptions)
defaulted._optimisticResults = client.isRestoring.value
? 'isRestoring'
: 'optimistic'

return defaulted
}),
return defaulted
},
),
)

const observer = new QueriesObserver<TCombinedResult>(
Expand Down

0 comments on commit a559c48

Please sign in to comment.