From d06e883318d412e1f410dd4820bd73eaac64966a Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Mon, 22 Jan 2024 01:35:02 +0900 Subject: [PATCH] fix(react-query): make @suspensive/react-query v2 all naming convention correctly like @tanstack/react-query v5 (#401) fix #253 # Overview ## PR Checklist - [x] I did below actions if need 1. I read the [Contributing Guide](https://github.com/suspensive/react/blob/main/CONTRIBUTING.md) 2. I added documents and tests. --- .changeset/eight-peas-hear.md | 5 + packages/react-query/src/index.ts | 16 +- .../src/useSuspenseInfiniteQuery.test-d.ts | 121 +++++----- .../src/useSuspenseInfiniteQuery.ts | 140 ++---------- .../src/useSuspenseQueries.test-d.ts | 213 +++--------------- .../react-query/src/useSuspenseQueries.ts | 135 ++++------- .../src/useSuspenseQuery.test-d.ts | 113 +++++----- packages/react-query/src/useSuspenseQuery.ts | 132 ++--------- 8 files changed, 228 insertions(+), 647 deletions(-) create mode 100644 .changeset/eight-peas-hear.md diff --git a/.changeset/eight-peas-hear.md b/.changeset/eight-peas-hear.md new file mode 100644 index 000000000..3346514be --- /dev/null +++ b/.changeset/eight-peas-hear.md @@ -0,0 +1,5 @@ +--- +'@suspensive/react-query': major +--- + +fix(react-query): @suspensive/react-query v2's all naming convention correctly like @tanstack/react-query v5 diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts index 6dd22514c..3bc1acd33 100644 --- a/packages/react-query/src/index.ts +++ b/packages/react-query/src/index.ts @@ -1,16 +1,8 @@ export { useSuspenseQuery } from './useSuspenseQuery' -export type { - BaseUseSuspenseQueryResult, - UseSuspenseQueryOptions, - UseSuspenseQueryResultOnLoading, - UseSuspenseQueryResultOnSuccess, -} from './useSuspenseQuery' +export type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from './useSuspenseQuery' export { useSuspenseQueries } from './useSuspenseQueries' +export type { SuspenseQueriesOptions, SuspenseQueriesResults } from './useSuspenseQueries' export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery' -export type { - BaseUseSuspenseInfiniteQueryResult, - UseSuspenseInfiniteQueryOptions, - UseSuspenseInfiniteQueryResultOnLoading, - UseSuspenseInfiniteQueryResultOnSuccess, -} from './useSuspenseInfiniteQuery' +export type { UseSuspenseInfiniteQueryOptions, UseSuspenseInfiniteQueryResult } from './useSuspenseInfiniteQuery' + export { QueryErrorBoundary } from './QueryErrorBoundary' diff --git a/packages/react-query/src/useSuspenseInfiniteQuery.test-d.ts b/packages/react-query/src/useSuspenseInfiniteQuery.test-d.ts index 7b036c49f..7628b57a7 100644 --- a/packages/react-query/src/useSuspenseInfiniteQuery.test-d.ts +++ b/packages/react-query/src/useSuspenseInfiniteQuery.test-d.ts @@ -1,73 +1,72 @@ /* eslint-disable react-hooks/rules-of-hooks */ import type { InfiniteData } from '@tanstack/react-query' -import { expectTypeOf } from 'vitest' +import { describe, expectTypeOf, it } from 'vitest' import { useSuspenseInfiniteQuery } from '../dist' const queryKey = ['key'] as const const queryFn = () => 'response' as const const boolean = Math.random() > 0.5 -type AwaitedQueryFnReturn = Awaited> +// @ts-expect-error no arg +useSuspenseInfiniteQuery() -// arg1:queryKey, arg2: queryFn, arg3: options -expectTypeOf( - useSuspenseInfiniteQuery(queryKey, queryFn, { - enabled: true, - }).data -).toEqualTypeOf>() -expectTypeOf( - useSuspenseInfiniteQuery(queryKey, queryFn, { - enabled: boolean, - }).data -).toEqualTypeOf | undefined>() -expectTypeOf( - useSuspenseInfiniteQuery(queryKey, queryFn, { - enabled: false, - }).data -).toEqualTypeOf() +// @ts-expect-error no suspense +useSuspenseInfiniteQuery({ queryKey, queryFn, suspense: boolean }) +// @ts-expect-error no suspense +useSuspenseInfiniteQuery(queryKey, { queryFn, suspense: boolean }) +// @ts-expect-error no suspense +useSuspenseInfiniteQuery(queryKey, queryFn, { suspense: boolean }) -// arg1:queryKey, arg2: options -expectTypeOf( - useSuspenseInfiniteQuery(queryKey, { - queryFn, - enabled: true, - }).data -).toEqualTypeOf>() -expectTypeOf( - useSuspenseInfiniteQuery(queryKey, { - queryFn, - enabled: boolean, - }).data -).toEqualTypeOf | undefined>() -expectTypeOf( - useSuspenseInfiniteQuery(queryKey, { - queryFn, - enabled: false, - }).data -).toEqualTypeOf() +// @ts-expect-error no useErrorBoundary +useSuspenseInfiniteQuery({ queryKey, queryFn, useErrorBoundary: boolean }) +// @ts-expect-error no useErrorBoundary +useSuspenseInfiniteQuery(queryKey, { queryFn, useErrorBoundary: boolean }) +// @ts-expect-error no useErrorBoundary +useSuspenseInfiniteQuery(queryKey, queryFn, { useErrorBoundary: boolean }) -// arg1: options -expectTypeOf( - useSuspenseInfiniteQuery({ - queryKey, - queryFn, - enabled: true, - }).data -).toEqualTypeOf>() -expectTypeOf( - useSuspenseInfiniteQuery({ - queryKey, - queryFn, - enabled: boolean, - }).data -).toEqualTypeOf | undefined>() -expectTypeOf( - useSuspenseInfiniteQuery({ - queryKey, - queryFn, - enabled: false, - }).data -).toEqualTypeOf() +// @ts-expect-error no enabled +useSuspenseInfiniteQuery({ queryKey, queryFn, enabled: boolean }) +// @ts-expect-error no enabled +useSuspenseInfiniteQuery(queryKey, { queryFn, enabled: boolean }) +// @ts-expect-error no enabled +useSuspenseInfiniteQuery(queryKey, queryFn, { enabled: boolean }) -// @ts-expect-error no arg -useSuspenseInfiniteQuery() +// @ts-expect-error no placeholderData +useSuspenseInfiniteQuery({ queryKey, queryFn, placeholderData: boolean }) +// @ts-expect-error no placeholderData +useSuspenseInfiniteQuery(queryKey, { queryFn, placeholderData: boolean }) +// @ts-expect-error no placeholderData +useSuspenseInfiniteQuery(queryKey, queryFn, { placeholderData: boolean }) + +// @ts-expect-error no isPlaceholderData +useSuspenseInfiniteQuery(queryKey, queryFn).isPlaceholderData +// @ts-expect-error no isPlaceholderData +useSuspenseInfiniteQuery(queryKey, queryFn, {}).isPlaceholderData +// @ts-expect-error no isPlaceholderData +useSuspenseInfiniteQuery(queryKey, { queryFn }).isPlaceholderData +// @ts-expect-error no isPlaceholderData +useSuspenseInfiniteQuery({ queryKey, queryFn }).isPlaceholderData + +describe('useSuspenseInfiniteQuery', () => { + it("'s type check", () => { + // data is always defined + expectTypeOf(useSuspenseInfiniteQuery(queryKey, queryFn).data).toEqualTypeOf< + InfiniteData>> + >() + expectTypeOf(useSuspenseInfiniteQuery(queryKey, queryFn, {}).data).toEqualTypeOf< + InfiniteData>> + >() + expectTypeOf(useSuspenseInfiniteQuery(queryKey, { queryFn }).data).toEqualTypeOf< + InfiniteData>> + >() + expectTypeOf(useSuspenseInfiniteQuery({ queryKey, queryFn }).data).toEqualTypeOf< + InfiniteData>> + >() + + // status is always 'success' + expectTypeOf(useSuspenseInfiniteQuery(queryKey, queryFn).status).toEqualTypeOf<'success'>() + expectTypeOf(useSuspenseInfiniteQuery(queryKey, queryFn, {}).status).toEqualTypeOf<'success'>() + expectTypeOf(useSuspenseInfiniteQuery(queryKey, { queryFn }).status).toEqualTypeOf<'success'>() + expectTypeOf(useSuspenseInfiniteQuery({ queryKey, queryFn }).status).toEqualTypeOf<'success'>() + }) +}) diff --git a/packages/react-query/src/useSuspenseInfiniteQuery.ts b/packages/react-query/src/useSuspenseInfiniteQuery.ts index 50d0cbd8b..6919acf6f 100644 --- a/packages/react-query/src/useSuspenseInfiniteQuery.ts +++ b/packages/react-query/src/useSuspenseInfiniteQuery.ts @@ -8,33 +8,27 @@ import { useInfiniteQuery, } from '@tanstack/react-query' -export type BaseUseSuspenseInfiniteQueryResult = Omit< - UseInfiniteQueryResult, - 'error' | 'isLoadingError' | 'isError' | 'isRefetchError' | 'isFetching' -> & { status: 'success' | 'loading' } - -export interface UseSuspenseInfiniteQueryResultOnSuccess extends BaseUseSuspenseInfiniteQueryResult { +export interface UseSuspenseInfiniteQueryResult + extends Omit< + UseInfiniteQueryResult, + keyof Pick, 'isPlaceholderData'> + > { data: InfiniteData - isLoading: false - isSuccess: true status: 'success' } -export interface UseSuspenseInfiniteQueryResultOnLoading extends BaseUseSuspenseInfiniteQueryResult { - data: undefined - isLoading: true - isSuccess: false - status: 'loading' -} -export type UseSuspenseInfiniteQueryOptions< +export interface UseSuspenseInfiniteQueryOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = Omit, 'suspense'> +> extends Omit< + UseInfiniteQueryOptions, + 'suspense' | 'useErrorBoundary' | 'enabled' | 'placeholderData' + > {} /** - * This hook wrapping useInfiniteQuery of @tanstack/react-query with default suspense option. with this hook, you don't have to make unnecessary type narrowing + * This hook is wrapping useInfiniteQuery of @tanstack/react-query v4 with default suspense option. * @see {@link https://suspensive.org/docs/react-query/useSuspenseInfiniteQuery} */ // arg1: queryKey, arg2: queryFn, arg3: options @@ -46,84 +40,9 @@ export function useSuspenseInfiniteQuery< >( queryKey: TQueryKey, queryFn: QueryFunction, - options?: Omit< - UseSuspenseInfiniteQueryOptions, - 'enabled' | 'queryKey' | 'queryFn' - > -): UseSuspenseInfiniteQueryResultOnSuccess -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options: Omit< - UseSuspenseInfiniteQueryOptions, - 'enabled' | 'queryKey' | 'queryFn' - > & { - enabled?: true - } -): UseSuspenseInfiniteQueryResultOnSuccess -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options: Omit< - UseSuspenseInfiniteQueryOptions, - 'enabled' | 'queryKey' | 'queryFn' - > & { - enabled: false - } -): UseSuspenseInfiniteQueryResultOnLoading -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options: Omit, 'queryKey' | 'queryFn'> -): UseSuspenseInfiniteQueryResultOnSuccess | UseSuspenseInfiniteQueryResultOnLoading - + options?: Omit, 'queryKey' | 'queryFn'> +): UseSuspenseInfiniteQueryResult // arg1: queryKey, arg2: options -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options?: Omit, 'enabled' | 'queryKey'> -): UseSuspenseInfiniteQueryResultOnSuccess -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options: Omit, 'enabled' | 'queryKey'> & { - enabled?: true - } -): UseSuspenseInfiniteQueryResultOnSuccess -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options: Omit, 'enabled' | 'queryKey'> & { - enabled: false - } -): UseSuspenseInfiniteQueryResultOnLoading export function useSuspenseInfiniteQuery< TQueryFnData = unknown, TError = unknown, @@ -132,29 +51,8 @@ export function useSuspenseInfiniteQuery< >( queryKey: TQueryKey, options: Omit, 'queryKey'> -): UseSuspenseInfiniteQueryResultOnSuccess | UseSuspenseInfiniteQueryResultOnLoading - +): UseSuspenseInfiniteQueryResult // arg1: options -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit, 'enabled'> & { - enabled?: true - } -): UseSuspenseInfiniteQueryResultOnSuccess -export function useSuspenseInfiniteQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit, 'enabled'> & { - enabled: false - } -): UseSuspenseInfiniteQueryResultOnLoading export function useSuspenseInfiniteQuery< TQueryFnData = unknown, TError = unknown, @@ -162,9 +60,7 @@ export function useSuspenseInfiniteQuery< TQueryKey extends QueryKey = QueryKey, >( options: UseSuspenseInfiniteQueryOptions -): UseSuspenseInfiniteQueryResultOnSuccess | UseSuspenseInfiniteQueryResultOnLoading - -// base useSuspenseInfiniteQuery +): UseSuspenseInfiniteQueryResult export function useSuspenseInfiniteQuery< TQueryFnData = unknown, TError = unknown, @@ -176,9 +72,11 @@ export function useSuspenseInfiniteQuery< | QueryFunction | Omit, 'queryKey'>, arg3?: Omit, 'queryKey' | 'queryFn'> -) { +): UseSuspenseInfiniteQueryResult { return useInfiniteQuery({ ...parseQueryArgs(arg1, arg2, arg3), + enabled: true, suspense: true, - }) as BaseUseSuspenseInfiniteQueryResult + useErrorBoundary: true, + }) as UseSuspenseInfiniteQueryResult } diff --git a/packages/react-query/src/useSuspenseQueries.test-d.ts b/packages/react-query/src/useSuspenseQueries.test-d.ts index c07a66f54..6f64fb3c5 100644 --- a/packages/react-query/src/useSuspenseQueries.test-d.ts +++ b/packages/react-query/src/useSuspenseQueries.test-d.ts @@ -1,196 +1,49 @@ /* eslint-disable react-hooks/rules-of-hooks */ -import { expectTypeOf } from 'vitest' +import { describe, expectTypeOf, it } from 'vitest' import { useSuspenseQueries } from '../dist' -const queryKey = ['key'] as const -const queryFn = () => 'response' as const -const boolean = Math.random() > 0.5 const select = () => 'selected' as const -type AwaitedQueryFnReturn = Awaited> -type SelectReturn = Awaited> - -// enabled: no -const [noEnabled, noEnabledSelect] = useSuspenseQueries({ - queries: [ - { queryKey, queryFn }, - { queryKey, queryFn, select }, - ] as const, -}) -expectTypeOf(noEnabled.data).toEqualTypeOf() -expectTypeOf(noEnabledSelect.data).toEqualTypeOf() -expectTypeOf(noEnabled.isLoading).toEqualTypeOf() -expectTypeOf(noEnabledSelect.isLoading).toEqualTypeOf() -expectTypeOf(noEnabled.isSuccess).toEqualTypeOf() -expectTypeOf(noEnabledSelect.isSuccess).toEqualTypeOf() -expectTypeOf(noEnabled.status).toEqualTypeOf<'success'>() -expectTypeOf(noEnabledSelect.status).toEqualTypeOf<'success'>() -// @ts-expect-error no isFetching -noEnabled.isFetching -// @ts-expect-error no isFetching -noEnabledSelect.isFetching -// @ts-expect-error no error -noEnabled.error -// @ts-expect-error no error -noEnabledSelect.error -// @ts-expect-error no isError -noEnabled.isError -// @ts-expect-error no isError -noEnabledSelect.isError -useSuspenseQueries({ - queries: [ - // @ts-expect-error no suspense - { queryKey, queryFn, suspense: false }, - // @ts-expect-error no suspense - { queryKey, queryFn, select, suspense: true }, - ] as const, -}) - -// enabled: undefined -const [undefinedEnabled, undefinedEnabledSelect] = useSuspenseQueries({ - queries: [ - { enabled: undefined, queryKey, queryFn }, - { enabled: undefined, queryKey, queryFn, select }, - ], -}) -expectTypeOf(undefinedEnabled.data).toEqualTypeOf() -expectTypeOf(undefinedEnabledSelect.data).toEqualTypeOf() -expectTypeOf(undefinedEnabled.isLoading).toEqualTypeOf() -expectTypeOf(undefinedEnabledSelect.isLoading).toEqualTypeOf() -expectTypeOf(undefinedEnabled.isSuccess).toEqualTypeOf() -expectTypeOf(undefinedEnabledSelect.isSuccess).toEqualTypeOf() -expectTypeOf(undefinedEnabled.status).toEqualTypeOf<'success'>() -expectTypeOf(undefinedEnabledSelect.status).toEqualTypeOf<'success'>() -// @ts-expect-error no isFetching -undefinedEnabled.isFetching -// @ts-expect-error no isFetching -undefinedEnabledSelect.isFetching -// @ts-expect-error no error -undefinedEnabled.error -// @ts-expect-error no error -undefinedEnabledSelect.error -// @ts-expect-error no isError -undefinedEnabled.isError -// @ts-expect-error no isError -undefinedEnabledSelect.isError -useSuspenseQueries({ - queries: [ - // @ts-expect-error no suspense - { enabled: undefined, queryKey, queryFn, suspense: false }, - // @ts-expect-error no suspense - { enabled: undefined, queryKey, queryFn, select, suspense: true }, - ] as const, -}) - -// enabled: true -const [trueEnabled, trueEnabledSelect] = useSuspenseQueries({ - queries: [ - { enabled: true, queryKey, queryFn }, - { enabled: true, queryKey, queryFn, select }, - ] as const, -}) -expectTypeOf(trueEnabled.data).toEqualTypeOf() -expectTypeOf(trueEnabledSelect.data).toEqualTypeOf() -expectTypeOf(trueEnabled.isLoading).toEqualTypeOf() -expectTypeOf(trueEnabledSelect.isLoading).toEqualTypeOf() -expectTypeOf(trueEnabled.isSuccess).toEqualTypeOf() -expectTypeOf(trueEnabledSelect.isSuccess).toEqualTypeOf() -expectTypeOf(trueEnabled.status).toEqualTypeOf<'success'>() -expectTypeOf(trueEnabledSelect.status).toEqualTypeOf<'success'>() -// @ts-expect-error no isFetching -trueEnabled.isFetching -// @ts-expect-error no isFetching -trueEnabledSelect.isFetching -// @ts-expect-error no error -trueEnabled.error -// @ts-expect-error no error -trueEnabledSelect.error -// @ts-expect-error no isError -trueEnabled.isError -// @ts-expect-error no isError -trueEnabledSelect.isError -useSuspenseQueries({ - queries: [ - // @ts-expect-error no suspense - { enabled: true, queryKey, queryFn, suspense: false }, - // @ts-expect-error no suspense - { enabled: true, queryKey, queryFn, select, suspense: true }, - ] as const, +const queryOptions = (index: TIndex) => ({ + queryKey: ['key', index] as const, + queryFn: () => ({ name: `resolved${index}` }) as const, }) -// enabled: false -const [falseEnabled, falseEnabledSelect] = useSuspenseQueries({ - queries: [ - { enabled: false, queryKey, queryFn }, - { enabled: false, queryKey, queryFn, select }, - ] as const, -}) -expectTypeOf(falseEnabled.data).toEqualTypeOf() -expectTypeOf(falseEnabledSelect.data).toEqualTypeOf() -expectTypeOf(falseEnabled.isLoading).toEqualTypeOf() -expectTypeOf(falseEnabledSelect.isLoading).toEqualTypeOf() -expectTypeOf(falseEnabled.isSuccess).toEqualTypeOf() -expectTypeOf(falseEnabledSelect.isSuccess).toEqualTypeOf() -expectTypeOf(falseEnabled.status).toEqualTypeOf<'loading'>() -expectTypeOf(falseEnabledSelect.status).toEqualTypeOf<'loading'>() -// @ts-expect-error no isFetching -falseEnabled.isFetching -// @ts-expect-error no isFetching -falseEnabledSelect.isFetching -// @ts-expect-error no error -falseEnabled.error -// @ts-expect-error no error -falseEnabledSelect.error -// @ts-expect-error no isError -falseEnabled.isError -// @ts-expect-error no isError -falseEnabledSelect.isError useSuspenseQueries({ queries: [ // @ts-expect-error no suspense - { enabled: false, queryKey, queryFn, suspense: false }, + { ...queryOptions(0), suspense: false }, // @ts-expect-error no suspense - { enabled: false, queryKey, queryFn, select, suspense: true }, + { ...queryOptions(1), select, suspense: true }, ] as const, }) -// enabled: boolean -const [booleanEnabled, booleanEnabledSelect] = useSuspenseQueries({ - queries: [ - { enabled: boolean, queryKey, queryFn }, - { enabled: boolean, queryKey, queryFn, select }, - ], -}) -expectTypeOf(booleanEnabled.data).toEqualTypeOf() -expectTypeOf(booleanEnabledSelect.data).toEqualTypeOf() -expectTypeOf(booleanEnabled.isLoading).toEqualTypeOf() -expectTypeOf(booleanEnabledSelect.isLoading).toEqualTypeOf() -expectTypeOf(booleanEnabled.isSuccess).toEqualTypeOf() -expectTypeOf(booleanEnabledSelect.isSuccess).toEqualTypeOf() -expectTypeOf(booleanEnabled.status).toEqualTypeOf<'success' | 'loading'>() -expectTypeOf(booleanEnabledSelect.status).toEqualTypeOf<'success' | 'loading'>() -// @ts-expect-error no isFetching -booleanEnabled.isFetching -// @ts-expect-error no isFetching -booleanEnabledSelect.isFetching -// @ts-expect-error no error -booleanEnabled.error -// @ts-expect-error no error -booleanEnabledSelect.error -// @ts-expect-error no isError -booleanEnabled.isError -// @ts-expect-error no isError -booleanEnabledSelect.isError -useSuspenseQueries({ - queries: [ - // @ts-expect-error no suspense - { enabled: boolean, queryKey, queryFn, suspense: false }, - // @ts-expect-error no suspense - { enabled: boolean, queryKey, queryFn, select, suspense: true }, - ] as const, -}) - -// @ts-expect-error noItem +// @ts-expect-error if no items useSuspenseQueries({}) -// @ts-expect-error noItem +// @ts-expect-error if no items useSuspenseQueries() + +describe('useSuspenseQueries', () => { + it("'s type check", () => { + const suspenseQueries = useSuspenseQueries({ + queries: [ + { ...queryOptions(0) }, + { ...queryOptions(1) }, + { ...queryOptions(2), select: () => ({ name: 'selected' }) as const }, + ] as const, + }) + + expectTypeOf(suspenseQueries[0].status).toEqualTypeOf<`success`>() + expectTypeOf(suspenseQueries[0].data).toEqualTypeOf< + Awaited>['queryFn']>> + >() + + expectTypeOf(suspenseQueries[1].status).toEqualTypeOf<`success`>() + expectTypeOf(suspenseQueries[1].data).toEqualTypeOf< + Awaited>['queryFn']>> + >() + + expectTypeOf(suspenseQueries[2].status).toEqualTypeOf<`success`>() + expectTypeOf(suspenseQueries[2].data).toEqualTypeOf<{ readonly name: 'selected' }>() + }) +}) diff --git a/packages/react-query/src/useSuspenseQueries.ts b/packages/react-query/src/useSuspenseQueries.ts index 1940b2f3c..8d14dc569 100644 --- a/packages/react-query/src/useSuspenseQueries.ts +++ b/packages/react-query/src/useSuspenseQueries.ts @@ -1,6 +1,5 @@ // TODO: remove this eslint /* eslint-disable @typescript-eslint/naming-convention */ - import { type QueryFunction, type QueryKey, @@ -8,123 +7,65 @@ import { type UseQueryResult, useQueries, } from '@tanstack/react-query' -import type { UseSuspenseQueryResultOnLoading, UseSuspenseQueryResultOnSuccess } from './useSuspenseQuery' +import type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from './useSuspenseQuery' // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 -type UseQueryOptionsForUseSuspenseQueries< - TQueryFnData = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TEnabled extends boolean | undefined = true, -> = Omit, 'context' | 'suspense'> & { - enabled?: TEnabled -} - -interface ToInferOption { +interface ToInferOptions { queryFn?: QueryFunction } -interface ToInferWithEnabledOption - extends ToInferOption { - enabled: TEnabled -} -interface ToInferWithSelectOption - extends ToInferOption { - select: (data: unknown) => TData -} -interface ToInferWithSelectEnabledOption - extends ToInferWithEnabledOption { + +interface ToInferWithSelectOptions + extends ToInferOptions { select: (data: unknown) => TData } -type GetOption = - // enabled: false - T extends ToInferWithSelectEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : T extends ToInferWithEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : // enabled: true - T extends ToInferWithSelectEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : T extends ToInferWithEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : // enabled: boolean - T extends ToInferWithSelectEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : T extends ToInferWithEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : // enabled: undefined - T extends ToInferWithSelectEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : T extends ToInferWithEnabledOption - ? UseQueryOptionsForUseSuspenseQueries - : // no enabled - T extends ToInferWithSelectOption - ? UseQueryOptionsForUseSuspenseQueries - : T extends ToInferOption - ? UseQueryOptionsForUseSuspenseQueries - : UseQueryOptionsForUseSuspenseQueries +type GetSuspenseOptions = + T extends ToInferWithSelectOptions + ? UseSuspenseQueryOptions + : T extends ToInferOptions + ? UseSuspenseQueryOptions + : UseSuspenseQueryOptions -type SuspenseQueriesOptions< +export type SuspenseQueriesOptions< T extends unknown[], TResult extends unknown[] = [], TDepth extends ReadonlyArray = [], > = TDepth['length'] extends MAXIMUM_DEPTH - ? UseQueryOptionsForUseSuspenseQueries[] + ? UseSuspenseQueryOptions[] : T extends [] ? [] : T extends [infer Head] - ? [...TResult, GetOption] + ? [...TResult, GetSuspenseOptions] : T extends [infer Head, ...infer Tail] - ? SuspenseQueriesOptions<[...Tail], [...TResult, GetOption], [...TDepth, 1]> + ? SuspenseQueriesOptions<[...Tail], [...TResult, GetSuspenseOptions], [...TDepth, 1]> : unknown[] extends T ? T - : T extends UseQueryOptionsForUseSuspenseQueries[] - ? UseQueryOptionsForUseSuspenseQueries[] - : UseQueryOptionsForUseSuspenseQueries[] + : T extends UseSuspenseQueryOptions[] + ? UseSuspenseQueryOptions[] + : UseSuspenseQueryOptions[] // results -type GetResult = T extends { queryFnData: any; data: infer TData } - ? UseSuspenseQueryResultOnSuccess +type GetSuspenseResult = T extends { queryFnData: any; data: infer TData } + ? UseSuspenseQueryResult : T extends { queryFnData: infer TQueryFnData } - ? UseSuspenseQueryResultOnSuccess + ? UseSuspenseQueryResult : T extends { data: infer TData } - ? UseSuspenseQueryResultOnSuccess + ? UseSuspenseQueryResult : T extends [any, infer TData] - ? UseSuspenseQueryResultOnSuccess + ? UseSuspenseQueryResult : T extends [infer TQueryFnData] - ? UseSuspenseQueryResultOnSuccess + ? UseSuspenseQueryResult : T extends [infer TQueryFnData] - ? UseSuspenseQueryResultOnSuccess - : // enabled: false - T extends ToInferWithSelectEnabledOption - ? UseSuspenseQueryResultOnLoading - : T extends ToInferWithEnabledOption - ? UseSuspenseQueryResultOnLoading - : // enabled: true - T extends ToInferWithSelectEnabledOption - ? UseSuspenseQueryResultOnSuccess - : T extends ToInferWithEnabledOption - ? UseSuspenseQueryResultOnSuccess - : // enabled: boolean - T extends ToInferWithSelectEnabledOption - ? UseSuspenseQueryResultOnSuccess | UseSuspenseQueryResultOnLoading - : T extends ToInferWithEnabledOption - ? UseSuspenseQueryResultOnSuccess | UseSuspenseQueryResultOnLoading - : // no enabled - T extends ToInferWithSelectOption - ? UseSuspenseQueryResultOnSuccess - : T extends ToInferOption - ? UseSuspenseQueryResultOnSuccess - : // enabled: undefined - T extends ToInferWithSelectEnabledOption - ? UseSuspenseQueryResultOnSuccess - : T extends ToInferWithEnabledOption - ? UseSuspenseQueryResultOnSuccess - : UseSuspenseQueryResultOnSuccess + ? UseSuspenseQueryResult + : T extends ToInferWithSelectOptions + ? UseSuspenseQueryResult + : T extends ToInferOptions + ? UseSuspenseQueryResult + : UseSuspenseQueryResult -export type QueriesResults< +export type SuspenseQueriesResults< T extends any[], TResult extends any[] = [], TDepth extends ReadonlyArray = [], @@ -133,17 +74,17 @@ export type QueriesResults< : T extends [] ? [] : T extends [infer Head] - ? [...TResult, GetResult] + ? [...TResult, GetSuspenseResult] : T extends [infer Head, ...infer Tail] - ? QueriesResults<[...Tail], [...TResult, GetResult], [...TDepth, 1]> - : T extends UseQueryOptionsForUseSuspenseQueries[] - ? UseSuspenseQueryResultOnSuccess[] - : (UseSuspenseQueryResultOnSuccess | UseSuspenseQueryResultOnLoading)[] + ? SuspenseQueriesResults<[...Tail], [...TResult, GetSuspenseResult], [...TDepth, 1]> + : T extends UseSuspenseQueryOptions[] + ? UseSuspenseQueryResult[] + : UseSuspenseQueryResult[] type UseSuspenseQueries = (arg: { queries: readonly [...SuspenseQueriesOptions] context?: UseQueryOptions['context'] -}) => QueriesResults +}) => SuspenseQueriesResults export const useSuspenseQueries: UseSuspenseQueries = ({ queries, context, @@ -154,4 +95,4 @@ export const useSuspenseQueries: UseSuspenseQueries = ({ useQueries({ queries: queries.map((query: typeof queries) => ({ ...query, suspense: true })), context, - }) as QueriesResults + }) as SuspenseQueriesResults diff --git a/packages/react-query/src/useSuspenseQuery.test-d.ts b/packages/react-query/src/useSuspenseQuery.test-d.ts index 314d1a6f3..0b2891d85 100644 --- a/packages/react-query/src/useSuspenseQuery.test-d.ts +++ b/packages/react-query/src/useSuspenseQuery.test-d.ts @@ -1,72 +1,63 @@ /* eslint-disable react-hooks/rules-of-hooks */ -import { expectTypeOf } from 'vitest' +import { describe, expectTypeOf, it } from 'vitest' import { useSuspenseQuery } from '../dist' const queryKey = ['key'] as const const queryFn = () => 'response' as const const boolean = Math.random() > 0.5 -type AwaitedQueryFnReturn = Awaited> +//@ts-expect-error no arg +useSuspenseQuery() -// arg1:queryKey, arg2: queryFn, arg3: options -expectTypeOf( - useSuspenseQuery(queryKey, queryFn, { - enabled: true, - }).data -).toEqualTypeOf() -expectTypeOf( - useSuspenseQuery(queryKey, queryFn, { - enabled: boolean, - }).data -).toEqualTypeOf() -expectTypeOf( - useSuspenseQuery(queryKey, queryFn, { - enabled: false, - }).data -).toEqualTypeOf() +//@ts-expect-error no suspense +useSuspenseQuery({ queryKey, queryFn, suspense: boolean }) +//@ts-expect-error no suspense +useSuspenseQuery(queryKey, { queryFn, suspense: boolean }) +//@ts-expect-error no suspense +useSuspenseQuery(queryKey, queryFn, { suspense: boolean }) -// arg1:queryKey, arg2: options -expectTypeOf( - useSuspenseQuery(queryKey, { - queryFn, - enabled: true, - }).data -).toEqualTypeOf() -expectTypeOf( - useSuspenseQuery(queryKey, { - queryFn, - enabled: boolean, - }).data -).toEqualTypeOf() -expectTypeOf( - useSuspenseQuery(queryKey, { - queryFn, - enabled: false, - }).data -).toEqualTypeOf() +//@ts-expect-error no useErrorBoundary +useSuspenseQuery({ queryKey, queryFn, useErrorBoundary: boolean }) +//@ts-expect-error no useErrorBoundary +useSuspenseQuery(queryKey, { queryFn, useErrorBoundary: boolean }) +//@ts-expect-error no useErrorBoundary +useSuspenseQuery(queryKey, queryFn, { useErrorBoundary: boolean }) -// arg1: options -expectTypeOf( - useSuspenseQuery({ - queryKey, - queryFn, - enabled: true, - }).data -).toEqualTypeOf() -expectTypeOf( - useSuspenseQuery({ - queryKey, - queryFn, - enabled: boolean, - }).data -).toEqualTypeOf() -expectTypeOf( - useSuspenseQuery({ - queryKey, - queryFn, - enabled: false, - }).data -).toEqualTypeOf() +//@ts-expect-error no enabled +useSuspenseQuery({ queryKey, queryFn, enabled: boolean }) +//@ts-expect-error no enabled +useSuspenseQuery(queryKey, { queryFn, enabled: boolean }) +//@ts-expect-error no enabled +useSuspenseQuery(queryKey, queryFn, { enabled: boolean }) -// @ts-expect-error no arg -useSuspenseQuery() +//@ts-expect-error no placeholderData +useSuspenseQuery({ queryKey, queryFn, placeholderData: boolean }) +//@ts-expect-error no placeholderData +useSuspenseQuery(queryKey, { queryFn, placeholderData: boolean }) +//@ts-expect-error no placeholderData +useSuspenseQuery(queryKey, queryFn, { placeholderData: boolean }) + +//@ts-expect-error no isPlaceholderData +useSuspenseQuery(queryKey, queryFn).isPlaceholderData +//@ts-expect-error no isPlaceholderData +useSuspenseQuery(queryKey, queryFn, {}).isPlaceholderData +//@ts-expect-error no isPlaceholderData +useSuspenseQuery(queryKey, { queryFn }).isPlaceholderData +//@ts-expect-error no isPlaceholderData +useSuspenseQuery({ queryKey, queryFn }).isPlaceholderData + +describe('useSuspenseQuery', () => { + it("'s data is always defined", () => { + expectTypeOf(useSuspenseQuery(queryKey, queryFn).data).toEqualTypeOf>>() + expectTypeOf(useSuspenseQuery(queryKey, queryFn, {}).data).toEqualTypeOf>>() + expectTypeOf(useSuspenseQuery(queryKey, { queryFn }).data).toEqualTypeOf>>() + expectTypeOf(useSuspenseQuery({ queryKey, queryFn }).data).toEqualTypeOf>>() + }) + + it("'s status is always 'success'", () => { + expectTypeOf(useSuspenseQuery(queryKey, queryFn).status).toEqualTypeOf<'success'>() + expectTypeOf(useSuspenseQuery(queryKey, queryFn, {}).status).toEqualTypeOf<'success'>() + expectTypeOf(useSuspenseQuery(queryKey, { queryFn }).status).toEqualTypeOf<'success'>() + expectTypeOf(useSuspenseQuery({ queryKey, queryFn }).status).toEqualTypeOf<'success'>() + }) +}) diff --git a/packages/react-query/src/useSuspenseQuery.ts b/packages/react-query/src/useSuspenseQuery.ts index 8eabebd2c..43a87923a 100644 --- a/packages/react-query/src/useSuspenseQuery.ts +++ b/packages/react-query/src/useSuspenseQuery.ts @@ -7,33 +7,24 @@ import { useQuery, } from '@tanstack/react-query' -export interface BaseUseSuspenseQueryResult - extends Omit, 'error' | 'isError' | 'isFetching'> { - status: 'success' | 'loading' -} - -export interface UseSuspenseQueryResultOnSuccess extends BaseUseSuspenseQueryResult { +export interface UseSuspenseQueryResult + extends Omit, keyof Pick> { data: TData - isLoading: false - isSuccess: true status: 'success' } -export interface UseSuspenseQueryResultOnLoading extends BaseUseSuspenseQueryResult { - data: undefined - isLoading: true - isSuccess: false - status: 'loading' -} -export type UseSuspenseQueryOptions< +export interface UseSuspenseQueryOptions< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = Omit, 'suspense'> +> extends Omit< + UseQueryOptions, + 'suspense' | 'useErrorBoundary' | 'enabled' | 'placeholderData' + > {} /** - * This hook wrapping useQuery of @tanstack/react-query with default suspense option. with this hook, you don't have to make unnecessary type narrowing + * This hook is wrapping useQuery of `@tanstack/react-query` v4 with default suspense option. * @see {@link https://suspensive.org/docs/react-query/useSuspenseQuery} */ // arg1: queryKey, arg2: queryFn, arg3: options @@ -45,43 +36,8 @@ export function useSuspenseQuery< >( queryKey: TQueryKey, queryFn: QueryFunction, - options?: Omit, 'enabled' | 'queryKey' | 'queryFn'> -): UseSuspenseQueryResultOnSuccess -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options: Omit, 'enabled' | 'queryKey' | 'queryFn'> & { - enabled?: true - } -): UseSuspenseQueryResultOnSuccess -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options: Omit, 'enabled' | 'queryKey' | 'queryFn'> & { - enabled: false - } -): UseSuspenseQueryResultOnLoading -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - queryFn: QueryFunction, - options: Omit, 'queryKey' | 'queryFn'> -): UseSuspenseQueryResultOnSuccess | UseSuspenseQueryResultOnLoading - + options?: Omit, 'queryKey' | 'queryFn'> +): UseSuspenseQueryResult // arg1: queryKey, arg2: options export function useSuspenseQuery< TQueryFnData = unknown, @@ -90,71 +46,15 @@ export function useSuspenseQuery< TQueryKey extends QueryKey = QueryKey, >( queryKey: TQueryKey, - options?: Omit, 'enabled' | 'queryKey'> -): UseSuspenseQueryResultOnSuccess -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options: Omit, 'enabled' | 'queryKey'> & { - enabled?: true - } -): UseSuspenseQueryResultOnSuccess -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options: Omit, 'enabled' | 'queryKey'> & { - enabled: false - } -): UseSuspenseQueryResultOnLoading -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - queryKey: TQueryKey, - options: Omit, 'queryKey'> -): UseSuspenseQueryResultOnSuccess | UseSuspenseQueryResultOnLoading - + options?: Omit, 'queryKey'> +): UseSuspenseQueryResult // arg1: options export function useSuspenseQuery< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, ->( - options: Omit, 'enabled'> & { - enabled?: true - } -): UseSuspenseQueryResultOnSuccess -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: Omit, 'enabled'> & { - enabled: false - } -): UseSuspenseQueryResultOnLoading -export function useSuspenseQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - options: UseSuspenseQueryOptions -): UseSuspenseQueryResultOnSuccess | UseSuspenseQueryResultOnLoading - -// base useSuspenseQuery +>(options: UseSuspenseQueryOptions): UseSuspenseQueryResult export function useSuspenseQuery< TQueryFnData = unknown, TError = unknown, @@ -167,8 +67,10 @@ export function useSuspenseQuery< | Omit, 'queryKey'>, arg3?: Omit, 'queryKey' | 'queryFn'> ) { - return useQuery({ + return useQuery({ ...parseQueryArgs(arg1, arg2, arg3), + enabled: true, + useErrorBoundary: true, suspense: true, - }) as BaseUseSuspenseQueryResult + }) as UseSuspenseQueryResult }