Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(react-query): prevent type errors and improve inference for dynamic queries on useQueries, useSuspenseQueries and createQueries #8624

Merged
merged 25 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a0e71f3
fix(react-query): prevent type errors and improve inference for dynam…
gs18004 Feb 8, 2025
a761dc9
fix(react-query): improve type inference for useQueries and useSuspen…
gs18004 Feb 9, 2025
df4aebb
fix(react-query): fix type of queries in useQueries and useSuspenseQu…
gs18004 Feb 9, 2025
30ee20b
refactor: remove meaningless depths
gs18004 Feb 10, 2025
e8d383a
Merge branch 'main' into bug/7974
TkDodo Feb 12, 2025
fdfd8b9
test(react-query): move type only tests
gs18004 Feb 12, 2025
0c007c4
test(react-query): fix type test error
gs18004 Feb 12, 2025
5e0b9e5
fix(vue-query): prevent type errors and improve inference for dynamic…
gs18004 Feb 12, 2025
caf92c2
Merge branch 'main' into bug/7974
TkDodo Feb 12, 2025
fb1b7b9
fix: prevent type errors and improve inference for dynamicqueries
gs18004 Feb 12, 2025
bbf5fde
fix: solid type tests
TkDodo Feb 14, 2025
71f27b6
fix: prevent type errors and improve inference for dynamic queries
gs18004 Feb 14, 2025
76344c2
chore: remove test
TkDodo Feb 14, 2025
fc823a1
Merge branch 'main' into bug/7974
TkDodo Feb 16, 2025
6b91ff0
test(react-query): fix failing type tests
gs18004 Feb 16, 2025
4d5306e
Merge branch 'main' into bug/7974
TkDodo Feb 16, 2025
9d7d7ca
Merge branch 'main' into bug/7974
gs18004 Feb 17, 2025
4e6ffb5
Merge branch 'main' into bug/7974
gs18004 Feb 18, 2025
37c4f3c
Merge branch 'main' into bug/7974
gs18004 Feb 18, 2025
ab45e8b
fix: fix test syntax error
gs18004 Feb 18, 2025
4a85362
test(react-query): remove tests for result[0].data
gs18004 Feb 18, 2025
65bbb85
Merge branch 'main' into bug/7974
gs18004 Feb 19, 2025
083e349
fix: change the result fallback to fix test fail
gs18004 Feb 19, 2025
08db211
Merge branch 'main' into bug/7974
TkDodo Feb 20, 2025
d3cce4c
rollback(angular-query-experimental): restore injectQueries
gs18004 Feb 21, 2025
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
61 changes: 61 additions & 0 deletions packages/react-query/src/__tests__/useQueries.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1621,4 +1621,65 @@ describe('useQueries', () => {
),
)
})

it('should return correct data for dynamic queries with mixed result types', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

type only tests should please go towards the dedicated .test-d.tsx files:

  • packages/react-query/src/__tests__/useQueries.test-d.tsx
  • packages/react-query/src/__tests__/useSuspenseQueries.test-d.tsx

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I will fix this.

const key1 = queryKey()
const key2 = queryKey()

function Page() {
const Queries1 = {
get: () =>
queryOptions({
queryKey: key1,
queryFn: async () => {
await sleep(10)
return 1
},
}),
}
const Queries2 = {
get: () =>
queryOptions({
queryKey: key2,
queryFn: async () => {
await sleep(10)
return true
},
}),
}

const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
const result = useQueries({
queries: [...queries1List, { ...Queries2.get() }],
})

expectTypeOf(result).toEqualTypeOf<
[
...Array<UseQueryResult<number, Error>>,
UseQueryResult<boolean, Error>,
]
>()

expectTypeOf(result[0]?.data).toEqualTypeOf<
number | boolean | undefined
>()

return (
<div>
<div>
data1: {String(result[0]?.data ?? 'null')}, data2:{' '}
{String(result[1]?.data ?? 'null')}, data3:{' '}
{String(result[2]?.data ?? 'null')}, data4:{' '}
{String(result[3]?.data ?? 'null')}
</div>
</div>
)
}

const rendered = renderWithClient(queryClient, <Page />)

await waitFor(() =>
rendered.getByText('data1: 1, data2: 1, data3: 1, data4: true'),
)
})
})
69 changes: 67 additions & 2 deletions packages/react-query/src/__tests__/useSuspenseQueries.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import {
beforeAll,
describe,
expect,
expectTypeOf,
it,
vi,
} from 'vitest'
import { act, fireEvent, render, waitFor } from '@testing-library/react'
import * as React from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { skipToken, useSuspenseQueries, useSuspenseQuery } from '..'
import {
queryOptions,
skipToken,
useSuspenseQueries,
useSuspenseQuery,
} from '..'
import { createQueryClient, queryKey, renderWithClient, sleep } from './utils'
import type { UseSuspenseQueryOptions } from '..'
import type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from '..'

type NumberQueryOptions = UseSuspenseQueryOptions<number>

Expand Down Expand Up @@ -693,4 +699,63 @@ describe('useSuspenseQueries 2', () => {
)
consoleErrorSpy.mockRestore()
})

it('should return correct data for dynamic queries with mixed result types', async () => {
const key1 = queryKey()
const key2 = queryKey()

function Page() {
const Queries1 = {
get: () =>
queryOptions({
queryKey: key1,
queryFn: async () => {
await sleep(10)
return 1
},
}),
}
const Queries2 = {
get: () =>
queryOptions({
queryKey: key2,
queryFn: async () => {
await sleep(10)
return true
},
}),
}

const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() }))
const result = useSuspenseQueries({
queries: [...queries1List, { ...Queries2.get() }],
})

expectTypeOf(result).toEqualTypeOf<
[
...Array<UseSuspenseQueryResult<number, Error>>,
UseSuspenseQueryResult<boolean, Error>,
]
>()

expectTypeOf(result[0]?.data).toEqualTypeOf<number | boolean>()

return (
<React.Suspense fallback="loading...">
<div>
data1: {String(result[0]?.data ?? 'null')}, data2:{' '}
{String(result[1]?.data ?? 'null')}, data3:{' '}
{String(result[2]?.data ?? 'null')}, data4:{' '}
{String(result[3]?.data ?? 'null')}
</div>
</React.Suspense>
)
}

const rendered = renderWithClient(queryClient, <Page />)

await waitFor(() =>
rendered.getByText('data1: 1, data2: 1, data3: 1, data4: true'),
)
})
})
32 changes: 15 additions & 17 deletions packages/react-query/src/useQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,23 +203,19 @@ export type QueriesResults<
[...TResults, GetUseQueryResult<Head>],
[...TDepth, 1]
>
: T extends Array<
UseQueryOptionsForUseQueries<
infer TQueryFnData,
infer TError,
infer TData,
any
>
>
? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results
Array<
UseQueryResult<
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: {
[K in keyof T]: T[K] extends UseQueryOptionsForUseQueries<
infer TQueryFnData,
infer TError,
infer TData,
any
>
: // Fallback
Array<UseQueryResult>
? UseQueryResult<
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: GetUseQueryResult<T[number]>
}

export function useQueries<
T extends Array<any>,
Expand All @@ -229,7 +225,9 @@ export function useQueries<
queries,
...options
}: {
queries: readonly [...QueriesOptions<T>]
queries:
| readonly [...QueriesOptions<T>]
| readonly [...{ [K in keyof T]: GetUseQueryOptionsForUseQueries<T[K]> }]
combine?: (result: QueriesResults<T>) => TCombinedResult
subscribed?: boolean
},
Expand Down
32 changes: 15 additions & 17 deletions packages/react-query/src/useSuspenseQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,30 +160,28 @@ export type SuspenseQueriesResults<
[...TResults, GetUseSuspenseQueryResult<Head>],
[...TDepth, 1]
>
: T extends Array<
UseSuspenseQueryOptions<
infer TQueryFnData,
infer TError,
infer TData,
any
>
>
? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results
Array<
UseSuspenseQueryResult<
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: {
[K in keyof T]: T[K] extends UseSuspenseQueryOptions<
infer TQueryFnData,
infer TError,
infer TData,
any
>
: // Fallback
Array<UseSuspenseQueryResult>
? UseSuspenseQueryResult<
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
: GetUseSuspenseQueryResult<T[number]>
}

export function useSuspenseQueries<
T extends Array<any>,
TCombinedResult = SuspenseQueriesResults<T>,
>(
options: {
queries: readonly [...SuspenseQueriesOptions<T>]
queries:
| readonly [...SuspenseQueriesOptions<T>]
| readonly [...{ [K in keyof T]: GetUseSuspenseQueryOptions<T[K]> }]
combine?: (result: SuspenseQueriesResults<T>) => TCombinedResult
},
queryClient?: QueryClient,
Expand Down
Loading