diff --git a/packages/react-query/src/__tests__/queryOptions.test-d.tsx b/packages/react-query/src/__tests__/queryOptions.test-d.tsx index 242be3972c..f019d883b3 100644 --- a/packages/react-query/src/__tests__/queryOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/queryOptions.test-d.tsx @@ -1,4 +1,4 @@ -import { describe, expect, expectTypeOf, it } from 'vitest' +import { describe, expectTypeOf, it } from 'vitest' import { QueriesObserver, QueryClient, @@ -71,14 +71,12 @@ describe('queryOptions', () => { expectTypeOf(data).toEqualTypeOf() }) it('should tag the queryKey with the result type of the QueryFn', () => { - expect(() => { - const { queryKey } = queryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - }) - - expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf() + const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), }) + + expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf() }) it('should tag the queryKey even if no promise is returned', () => { const { queryKey } = queryOptions({ diff --git a/packages/svelte-query/src/createQueries.ts b/packages/svelte-query/src/createQueries.ts index 6e16ec40af..cb1ba75679 100644 --- a/packages/svelte-query/src/createQueries.ts +++ b/packages/svelte-query/src/createQueries.ts @@ -7,6 +7,7 @@ import type { Readable } from 'svelte/store' import type { StoreOrVal } from './types' import type { DefaultError, + DefinedQueryObserverResult, OmitKeyof, QueriesObserverOptions, QueriesPlaceholderDataFunction, @@ -19,7 +20,7 @@ import type { } from '@tanstack/query-core' // This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. -// `placeholderData` function does not have a parameter +// `placeholderData` function always gets undefined passed type QueryObserverOptionsForCreateQueries< TQueryFnData = unknown, TError = DefaultError, @@ -38,7 +39,7 @@ type MAXIMUM_DEPTH = 20 // Widen the type of the symbol to enable type inference even if skipToken is not immutable. type SkipTokenForUseQueries = symbol -type GetOptions = +type GetQueryObserverOptionsForCreateQueries = // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData } T extends { queryFnData: infer TQueryFnData @@ -74,21 +75,38 @@ type GetOptions = : // Fallback QueryObserverOptionsForCreateQueries -type GetResults = +// A defined initialData setting should return a DefinedQueryObserverResult rather than CreateQueryResult +type GetDefinedOrUndefinedQueryResult = T extends { + initialData?: infer TInitialData +} + ? unknown extends TInitialData + ? QueryObserverResult + : TInitialData extends TData + ? DefinedQueryObserverResult + : TInitialData extends () => infer TInitialDataResult + ? unknown extends TInitialDataResult + ? QueryObserverResult + : TInitialDataResult extends TData + ? DefinedQueryObserverResult + : QueryObserverResult + : QueryObserverResult + : QueryObserverResult + +type GetCreateQueryResult = // Part 1: responsible for mapping explicit type parameter to function result, if object T extends { queryFnData: any; error?: infer TError; data: infer TData } - ? QueryObserverResult + ? GetDefinedOrUndefinedQueryResult : T extends { queryFnData: infer TQueryFnData; error?: infer TError } - ? QueryObserverResult + ? GetDefinedOrUndefinedQueryResult : T extends { data: infer TData; error?: infer TError } - ? QueryObserverResult + ? GetDefinedOrUndefinedQueryResult : // Part 2: responsible for mapping explicit type parameter to function result, if tuple T extends [any, infer TError, infer TData] - ? QueryObserverResult + ? GetDefinedOrUndefinedQueryResult : T extends [infer TQueryFnData, infer TError] - ? QueryObserverResult + ? GetDefinedOrUndefinedQueryResult : T extends [infer TQueryFnData] - ? QueryObserverResult + ? GetDefinedOrUndefinedQueryResult : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided T extends { queryFn?: @@ -97,7 +115,8 @@ type GetResults = select?: (data: any) => infer TData throwOnError?: ThrowOnError } - ? QueryObserverResult< + ? GetDefinedOrUndefinedQueryResult< + T, unknown extends TData ? TQueryFnData : TData, unknown extends TError ? DefaultError : TError > @@ -109,18 +128,18 @@ type GetResults = */ export type QueriesOptions< T extends Array, - TResult extends Array = [], + TResults extends Array = [], TDepth extends ReadonlyArray = [], > = TDepth['length'] extends MAXIMUM_DEPTH ? Array : T extends [] ? [] : T extends [infer Head] - ? [...TResult, GetOptions] - : T extends [infer Head, ...infer Tail] + ? [...TResults, GetQueryObserverOptionsForCreateQueries] + : T extends [infer Head, ...infer Tails] ? QueriesOptions< - [...Tail], - [...TResult, GetOptions], + [...Tails], + [...TResults, GetQueryObserverOptionsForCreateQueries], [...TDepth, 1] > : ReadonlyArray extends T @@ -151,18 +170,18 @@ export type QueriesOptions< */ export type QueriesResults< T extends Array, - TResult extends Array = [], + TResults extends Array = [], TDepth extends ReadonlyArray = [], > = TDepth['length'] extends MAXIMUM_DEPTH ? Array : T extends [] ? [] : T extends [infer Head] - ? [...TResult, GetResults] - : T extends [infer Head, ...infer Tail] + ? [...TResults, GetCreateQueryResult] + : T extends [infer Head, ...infer Tails] ? QueriesResults< - [...Tail], - [...TResult, GetResults], + [...Tails], + [...TResults, GetCreateQueryResult], [...TDepth, 1] > : T extends Array< diff --git a/packages/svelte-query/src/queryOptions.ts b/packages/svelte-query/src/queryOptions.ts index 7e958f3df7..94c75e8056 100644 --- a/packages/svelte-query/src/queryOptions.ts +++ b/packages/svelte-query/src/queryOptions.ts @@ -29,8 +29,8 @@ export function queryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: UndefinedInitialDataOptions, -): UndefinedInitialDataOptions & { + options: DefinedInitialDataOptions, +): DefinedInitialDataOptions & { queryKey: DataTag } @@ -40,8 +40,8 @@ export function queryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: DefinedInitialDataOptions, -): DefinedInitialDataOptions & { + options: UndefinedInitialDataOptions, +): UndefinedInitialDataOptions & { queryKey: DataTag } diff --git a/packages/svelte-query/tests/createQueries/createQueries.test-d.ts b/packages/svelte-query/tests/createQueries/createQueries.test-d.ts new file mode 100644 index 0000000000..32721b2f49 --- /dev/null +++ b/packages/svelte-query/tests/createQueries/createQueries.test-d.ts @@ -0,0 +1,24 @@ +import { describe, expectTypeOf, test } from 'vitest' +import { get } from 'svelte/store' +import { createQueries, queryOptions } from '../../src/index' + +describe('createQueries', () => { + test('TData should be defined when passed through queryOptions', () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: { + wow: true, + }, + }) + const queryResults = createQueries({ queries: [options] }) + + const data = get(queryResults)[0].data + + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() + }) +}) diff --git a/packages/svelte-query/tests/infiniteQueryOptions/infiniteQueryOptions.test-d.ts b/packages/svelte-query/tests/infiniteQueryOptions/infiniteQueryOptions.test-d.ts new file mode 100644 index 0000000000..0b4af0992e --- /dev/null +++ b/packages/svelte-query/tests/infiniteQueryOptions/infiniteQueryOptions.test-d.ts @@ -0,0 +1,60 @@ +import { describe, expectTypeOf, test } from 'vitest' +import { get } from 'svelte/store' +import { QueryClient } from '@tanstack/query-core' +import { createInfiniteQuery, infiniteQueryOptions } from '../../src/index' +import type { InfiniteData } from '@tanstack/query-core' + +describe('queryOptions', () => { + test('Should not allow excess properties', () => { + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('data'), + getNextPageParam: () => 1, + initialPageParam: 1, + // @ts-expect-error this is a good error, because stallTime does not exist! + stallTime: 1000, + }) + }) + + test('Should infer types for callbacks', () => { + infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('data'), + staleTime: 1000, + getNextPageParam: () => 1, + initialPageParam: 1, + select: (data) => { + expectTypeOf(data).toEqualTypeOf>() + }, + }) + }) + + test('Should work when passed to createInfiniteQuery', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + }) + + const query = createInfiniteQuery(options) + + // known issue: type of pageParams is unknown when returned from useInfiniteQuery + expectTypeOf(get(query).data).toEqualTypeOf< + InfiniteData | undefined + >() + }) + + test('Should work when passed to fetchInfiniteQuery', async () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + }) + + const data = await new QueryClient().fetchInfiniteQuery(options) + + expectTypeOf(data).toEqualTypeOf>() + }) +}) diff --git a/packages/svelte-query/tests/infiniteQueryOptions/infiniteQueryOptions.test.ts b/packages/svelte-query/tests/infiniteQueryOptions/infiniteQueryOptions.test.ts deleted file mode 100644 index 18b048f2c9..0000000000 --- a/packages/svelte-query/tests/infiniteQueryOptions/infiniteQueryOptions.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, expectTypeOf, test } from 'vitest' -import { type InfiniteData } from '@tanstack/query-core' -import { infiniteQueryOptions } from '../../src/infiniteQueryOptions' - -describe('queryOptions', () => { - test('Should not allow excess properties', () => { - infiniteQueryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve('data'), - getNextPageParam: () => 1, - initialPageParam: 1, - // @ts-expect-error this is a good error, because stallTime does not exist! - stallTime: 1000, - }) - }) - test('Should infer types for callbacks', () => { - infiniteQueryOptions({ - queryKey: ['key'], - queryFn: () => Promise.resolve('data'), - staleTime: 1000, - getNextPageParam: () => 1, - initialPageParam: 1, - select: (data) => { - expectTypeOf(data).toEqualTypeOf>() - }, - }) - }) -}) diff --git a/packages/svelte-query/tests/queryOptions/queryOptions.test.ts b/packages/svelte-query/tests/queryOptions/queryOptions.test-d.ts similarity index 81% rename from packages/svelte-query/tests/queryOptions/queryOptions.test.ts rename to packages/svelte-query/tests/queryOptions/queryOptions.test-d.ts index 02ca7b1869..4999037005 100644 --- a/packages/svelte-query/tests/queryOptions/queryOptions.test.ts +++ b/packages/svelte-query/tests/queryOptions/queryOptions.test-d.ts @@ -1,6 +1,13 @@ -import { QueryClient, dataTagSymbol, skipToken } from '@tanstack/query-core' import { describe, expectTypeOf, test } from 'vitest' -import { queryOptions } from '../../src/queryOptions' +import { get } from 'svelte/store' +import { + QueriesObserver, + QueryClient, + dataTagSymbol, + skipToken, +} from '@tanstack/query-core' +import { createQueries, queryOptions } from '../../src/index' +import type { QueryObserverResult } from '@tanstack/query-core' describe('queryOptions', () => { test('Should not allow excess properties', () => { @@ -33,6 +40,19 @@ describe('queryOptions', () => { expectTypeOf(data).toEqualTypeOf() }) + test('Should work when passed to createQueries', () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + }) + + const queries = createQueries({ + queries: [options], + }) + + expectTypeOf(get(queries)[0].data).toEqualTypeOf() + }) + test('Should tag the queryKey with the result type of the QueryFn', () => { const { queryKey } = queryOptions({ queryKey: ['key'], @@ -143,4 +163,17 @@ describe('queryOptions', () => { const data = queryClient.getQueryData(options.queryKey) expectTypeOf(data).toEqualTypeOf() }) + + test('Should return the proper type when passed to QueriesObserver', () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + }) + + const queryClient = new QueryClient() + const queriesObserver = new QueriesObserver(queryClient, [options]) + expectTypeOf(queriesObserver).toEqualTypeOf< + QueriesObserver> + >() + }) })