Skip to content

Commit

Permalink
refactor(types): throw type error when skipToken is present in susp…
Browse files Browse the repository at this point in the history
…ense query (#8082)

* refactor: handle type error when skipToken is present in suspense query

* test(react-query): add skipToken test for query hooks

* fix: merge form main

* ci: apply automated fixes

* feat(react-query): display “skipToken is not allowed” message in suspense hooks

* feat(react-query): display “skipToken is not allowed” message in suspense hooks

* feat(react-query): display “skipToken is not allowed” message in suspense hooks

* fix(react-query): update error message in useSuspenseInfiniteQuery

* test(react-query): add test case

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dominik Dorfmeister <[email protected]>
  • Loading branch information
3 people authored Oct 11, 2024
1 parent c635194 commit a991d92
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expectTypeOf, it, test } from 'vitest'
import { QueryClient, dataTagSymbol } from '@tanstack/query-core'
import { QueryClient, dataTagSymbol, skipToken } from '@tanstack/query-core'
import { infiniteQueryOptions } from '../infiniteQueryOptions'
import { useInfiniteQuery } from '../useInfiniteQuery'
import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
Expand Down Expand Up @@ -134,6 +134,18 @@ describe('infiniteQueryOptions', () => {
InfiniteData<string, unknown> | undefined
>()
})
it('should throw a type error when using queryFn with skipToken in a suspense query', () => {
const options = infiniteQueryOptions({
queryKey: ['key'],
queryFn:
Math.random() > 0.5 ? skipToken : () => Promise.resolve('string'),
getNextPageParam: () => 1,
initialPageParam: 1,
})
// @ts-expect-error TS2345
const { data } = useSuspenseInfiniteQuery(options)
expectTypeOf(data).toEqualTypeOf<InfiniteData<string, unknown>>()
})

test('should not be allowed to be passed to non-infinite query functions', () => {
const queryClient = new QueryClient()
Expand Down
10 changes: 7 additions & 3 deletions packages/react-query/src/__tests__/prefetch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
} from '..'
import { createQueryClient, queryKey, renderWithClient, sleep } from './utils'

import type { InfiniteData, UseInfiniteQueryOptions, UseQueryOptions } from '..'
import type {
InfiniteData,
UseSuspenseInfiniteQueryOptions,
UseSuspenseQueryOptions,
} from '..'
import type { Mock } from 'vitest'

const generateQueryFn = (data: string) =>
Expand Down Expand Up @@ -56,7 +60,7 @@ describe('usePrefetchQuery', () => {
const queryClient = createQueryClient({ queryCache })

function Suspended<TData = unknown>(props: {
queryOpts: UseQueryOptions<TData, Error, TData, Array<string>>
queryOpts: UseSuspenseQueryOptions<TData, Error, TData, Array<string>>
children?: React.ReactNode
}) {
const state = useSuspenseQuery(props.queryOpts)
Expand Down Expand Up @@ -303,7 +307,7 @@ describe('usePrefetchInfiniteQuery', () => {
const Fallback = vi.fn().mockImplementation(() => <div>Loading...</div>)

function Suspended<T = unknown>(props: {
queryOpts: UseInfiniteQueryOptions<
queryOpts: UseSuspenseInfiniteQueryOptions<
T,
Error,
InfiniteData<T>,
Expand Down
10 changes: 10 additions & 0 deletions packages/react-query/src/__tests__/queryOptions.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ describe('queryOptions', () => {
expectTypeOf(data).toEqualTypeOf<unknown>()
})

it('should throw a type error when using queryFn with skipToken in a suspense query', () => {
const options = queryOptions({
queryKey: ['key'],
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
})
// @ts-expect-error TS2345
const { data } = useSuspenseQuery(options)
expectTypeOf(data).toEqualTypeOf<number>()
})

it('should return the proper type when passed to QueriesObserver', () => {
const options = queryOptions({
queryKey: ['key'],
Expand Down
40 changes: 29 additions & 11 deletions packages/react-query/src/__tests__/suspense.test-d.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expectTypeOf, it } from 'vitest'
import { skipToken } from '@tanstack/query-core'
import { useSuspenseQuery } from '../useSuspenseQuery'
import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
import type { UseSuspenseQueryOptions } from '..'
import type { InfiniteData } from '@tanstack/query-core'

describe('useSuspenseQuery', () => {
Expand All @@ -23,6 +23,20 @@ describe('useSuspenseQuery', () => {
expectTypeOf(status).toEqualTypeOf<'error' | 'success'>()
})

it('should not allow skipToken in queryFn', () => {
useSuspenseQuery({
queryKey: ['key'],
// @ts-expect-error
queryFn: skipToken,
})

useSuspenseQuery({
queryKey: ['key'],
// @ts-expect-error
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
})
})

it('should not allow placeholderData, enabled or throwOnError props', () => {
useSuspenseQuery({
queryKey: ['key'],
Expand Down Expand Up @@ -70,6 +84,20 @@ describe('useSuspenseInfiniteQuery', () => {
expectTypeOf(data).toEqualTypeOf<InfiniteData<number, unknown>>()
})

it('should not allow skipToken in queryFn', () => {
useSuspenseInfiniteQuery({
queryKey: ['key'],
// @ts-expect-error
queryFn: skipToken,
})

useSuspenseInfiniteQuery({
queryKey: ['key'],
// @ts-expect-error
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
})
})

it('should not have pending status', () => {
const { status } = useSuspenseInfiniteQuery({
queryKey: ['key'],
Expand Down Expand Up @@ -122,14 +150,4 @@ describe('useSuspenseInfiniteQuery', () => {
// @ts-expect-error TS2339
query.isPlaceholderData
})

it('should not accept skipToken type for queryFn in useSuspenseQuery', () => {
const query: UseSuspenseQueryOptions = {
// @ts-expect-error
queryFn: skipToken,
queryKey: [1],
}

return query
})
})
59 changes: 41 additions & 18 deletions packages/react-query/src/__tests__/useSuspenseQueries.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,47 @@ describe('UseSuspenseQueries config object overload', () => {
expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>()
})

it('should not allow skipToken in queryFn', () => {
useSuspenseQueries({
queries: [
{
queryKey: ['key'],
// @ts-expect-error
queryFn: skipToken,
},
],
})

useSuspenseQueries({
queries: [
{
queryKey: ['key'],
// @ts-expect-error
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
},
],
})
})

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

const firstResult = queryResults[0]

expectTypeOf(firstResult).toEqualTypeOf<
UseSuspenseQueryResult<number, Error>
>()
expectTypeOf(firstResult.data).toEqualTypeOf<number>()
})

describe('custom hook', () => {
it('should allow custom hooks using UseQueryOptions', () => {
type Data = string
Expand All @@ -113,22 +154,4 @@ describe('UseSuspenseQueries config object overload', () => {
expectTypeOf(data).toEqualTypeOf<Data>()
})
})

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

const firstResult = queryResults[0]

expectTypeOf(firstResult).toEqualTypeOf<
UseSuspenseQueryResult<number, Error>
>()
expectTypeOf(firstResult.data).toEqualTypeOf<number>()
})
})
2 changes: 2 additions & 0 deletions packages/react-query/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ export { queryOptions } from './queryOptions'
export type {
DefinedInitialDataOptions,
UndefinedInitialDataOptions,
UnusedSkipTokenOptions,
} from './queryOptions'
export { infiniteQueryOptions } from './infiniteQueryOptions'
export type {
DefinedInitialDataInfiniteOptions,
UndefinedInitialDataInfiniteOptions,
UnusedSkipTokenInfiniteOptions,
} from './infiniteQueryOptions'
export {
QueryClientContext,
Expand Down
56 changes: 56 additions & 0 deletions packages/react-query/src/infiniteQueryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import type {
DefaultError,
InfiniteData,
InitialDataFunction,
OmitKeyof,
QueryKey,
SkipToken,
} from '@tanstack/query-core'
import type { UseInfiniteQueryOptions } from './types'

Expand All @@ -29,6 +31,36 @@ export type UndefinedInitialDataInfiniteOptions<
>
}

export type UnusedSkipTokenInfiniteOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> = OmitKeyof<
UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey,
TPageParam
>,
'queryFn'
> & {
queryFn: Exclude<
UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey,
TPageParam
>['queryFn'],
SkipToken
>
}

type NonUndefinedGuard<T> = T extends undefined ? never : T

export type DefinedInitialDataInfiniteOptions<
Expand Down Expand Up @@ -75,6 +107,30 @@ export function infiniteQueryOptions<
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
}

export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UnusedSkipTokenInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): UnusedSkipTokenInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
}

export function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
Expand Down
28 changes: 28 additions & 0 deletions packages/react-query/src/queryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import type {
DataTag,
DefaultError,
InitialDataFunction,
OmitKeyof,
QueryKey,
SkipToken,
} from '@tanstack/query-core'
import type { UseQueryOptions } from './types'

Expand All @@ -18,6 +20,21 @@ export type UndefinedInitialDataOptions<
| NonUndefinedGuard<TQueryFnData>
}

export type UnusedSkipTokenOptions<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = OmitKeyof<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryFn'
> & {
queryFn: Exclude<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
SkipToken
>
}

type NonUndefinedGuard<T> = T extends undefined ? never : T

export type DefinedInitialDataOptions<
Expand All @@ -42,6 +59,17 @@ export function queryOptions<
queryKey: DataTag<TQueryKey, TQueryFnData>
}

export function queryOptions<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey>,
): UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey> & {
queryKey: DataTag<TQueryKey, TQueryFnData>
}

export function queryOptions<
TQueryFnData = unknown,
TError = DefaultError,
Expand Down
26 changes: 22 additions & 4 deletions packages/react-query/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
QueryKey,
QueryObserverOptions,
QueryObserverResult,
SkipToken,
} from '@tanstack/query-core'

export interface UseBaseQueryOptions<
Expand Down Expand Up @@ -47,8 +48,13 @@ export interface UseSuspenseQueryOptions<
TQueryKey extends QueryKey = QueryKey,
> extends OmitKeyof<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'enabled' | 'throwOnError' | 'placeholderData'
> {}
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
> {
queryFn: Exclude<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
SkipToken
>
}

export interface UseInfiniteQueryOptions<
TQueryFnData = unknown,
Expand Down Expand Up @@ -85,8 +91,20 @@ export interface UseSuspenseInfiniteQueryOptions<
TQueryKey,
TPageParam
>,
'enabled' | 'throwOnError' | 'placeholderData'
> {}
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
> {
queryFn: Exclude<
UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey,
TPageParam
>['queryFn'],
SkipToken
>
}

export type UseBaseQueryResult<
TData = unknown,
Expand Down
Loading

0 comments on commit a991d92

Please sign in to comment.