Skip to content

Commit

Permalink
Fix createQueries initialData type
Browse files Browse the repository at this point in the history
  • Loading branch information
lachlancollins committed Jul 20, 2024
1 parent e4e65be commit 837bb0e
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 62 deletions.
14 changes: 6 additions & 8 deletions packages/react-query/src/__tests__/queryOptions.test-d.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, expectTypeOf, it } from 'vitest'
import { describe, expectTypeOf, it } from 'vitest'
import {
QueriesObserver,
QueryClient,
Expand Down Expand Up @@ -71,14 +71,12 @@ describe('queryOptions', () => {
expectTypeOf(data).toEqualTypeOf<number | undefined>()
})
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<number>()
const { queryKey } = queryOptions({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
})

expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
})
it('should tag the queryKey even if no promise is returned', () => {
const { queryKey } = queryOptions({
Expand Down
59 changes: 39 additions & 20 deletions packages/svelte-query/src/createQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Readable } from 'svelte/store'
import type { StoreOrVal } from './types'
import type {
DefaultError,
DefinedQueryObserverResult,
OmitKeyof,
QueriesObserverOptions,
QueriesPlaceholderDataFunction,
Expand All @@ -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,
Expand All @@ -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<T> =
type GetQueryObserverOptionsForCreateQueries<T> =
// Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
T extends {
queryFnData: infer TQueryFnData
Expand Down Expand Up @@ -74,21 +75,38 @@ type GetOptions<T> =
: // Fallback
QueryObserverOptionsForCreateQueries

type GetResults<T> =
// A defined initialData setting should return a DefinedQueryObserverResult rather than CreateQueryResult
type GetDefinedOrUndefinedQueryResult<T, TData, TError = unknown> = T extends {
initialData?: infer TInitialData
}
? unknown extends TInitialData
? QueryObserverResult<TData, TError>
: TInitialData extends TData
? DefinedQueryObserverResult<TData, TError>
: TInitialData extends () => infer TInitialDataResult
? unknown extends TInitialDataResult
? QueryObserverResult<TData, TError>
: TInitialDataResult extends TData
? DefinedQueryObserverResult<TData, TError>
: QueryObserverResult<TData, TError>
: QueryObserverResult<TData, TError>
: QueryObserverResult<TData, TError>

type GetCreateQueryResult<T> =
// Part 1: responsible for mapping explicit type parameter to function result, if object
T extends { queryFnData: any; error?: infer TError; data: infer TData }
? QueryObserverResult<TData, TError>
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
? QueryObserverResult<TQueryFnData, TError>
? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError>
: T extends { data: infer TData; error?: infer TError }
? QueryObserverResult<TData, TError>
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
: // Part 2: responsible for mapping explicit type parameter to function result, if tuple
T extends [any, infer TError, infer TData]
? QueryObserverResult<TData, TError>
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
: T extends [infer TQueryFnData, infer TError]
? QueryObserverResult<TQueryFnData, TError>
? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError>
: T extends [infer TQueryFnData]
? QueryObserverResult<TQueryFnData>
? GetDefinedOrUndefinedQueryResult<T, TQueryFnData>
: // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
T extends {
queryFn?:
Expand All @@ -97,7 +115,8 @@ type GetResults<T> =
select?: (data: any) => infer TData
throwOnError?: ThrowOnError<any, infer TError, any, any>
}
? QueryObserverResult<
? GetDefinedOrUndefinedQueryResult<
T,
unknown extends TData ? TQueryFnData : TData,
unknown extends TError ? DefaultError : TError
>
Expand All @@ -109,18 +128,18 @@ type GetResults<T> =
*/
export type QueriesOptions<
T extends Array<any>,
TResult extends Array<any> = [],
TResults extends Array<any> = [],
TDepth extends ReadonlyArray<number> = [],
> = TDepth['length'] extends MAXIMUM_DEPTH
? Array<QueryObserverOptionsForCreateQueries>
: T extends []
? []
: T extends [infer Head]
? [...TResult, GetOptions<Head>]
: T extends [infer Head, ...infer Tail]
? [...TResults, GetQueryObserverOptionsForCreateQueries<Head>]
: T extends [infer Head, ...infer Tails]
? QueriesOptions<
[...Tail],
[...TResult, GetOptions<Head>],
[...Tails],
[...TResults, GetQueryObserverOptionsForCreateQueries<Head>],
[...TDepth, 1]
>
: ReadonlyArray<unknown> extends T
Expand Down Expand Up @@ -151,18 +170,18 @@ export type QueriesOptions<
*/
export type QueriesResults<
T extends Array<any>,
TResult extends Array<any> = [],
TResults extends Array<any> = [],
TDepth extends ReadonlyArray<number> = [],
> = TDepth['length'] extends MAXIMUM_DEPTH
? Array<QueryObserverResult>
: T extends []
? []
: T extends [infer Head]
? [...TResult, GetResults<Head>]
: T extends [infer Head, ...infer Tail]
? [...TResults, GetCreateQueryResult<Head>]
: T extends [infer Head, ...infer Tails]
? QueriesResults<
[...Tail],
[...TResult, GetResults<Head>],
[...Tails],
[...TResults, GetCreateQueryResult<Head>],
[...TDepth, 1]
>
: T extends Array<
Expand Down
8 changes: 4 additions & 4 deletions packages/svelte-query/src/queryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export function queryOptions<
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & {
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
): DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & {
queryKey: DataTag<TQueryKey, TQueryFnData>
}

Expand All @@ -40,8 +40,8 @@ export function queryOptions<
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
): DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & {
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & {
queryKey: DataTag<TQueryKey, TQueryFnData>
}

Expand Down
24 changes: 24 additions & 0 deletions packages/svelte-query/tests/createQueries/createQueries.test-d.ts
Original file line number Diff line number Diff line change
@@ -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 }>()
})
})
Original file line number Diff line number Diff line change
@@ -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<InfiniteData<string, number>>()
},
})
})

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<string, unknown> | 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<InfiniteData<string, number>>()
})
})

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -33,6 +40,19 @@ describe('queryOptions', () => {
expectTypeOf(data).toEqualTypeOf<number>()
})

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<number | undefined>()
})

test('Should tag the queryKey with the result type of the QueryFn', () => {
const { queryKey } = queryOptions({
queryKey: ['key'],
Expand Down Expand Up @@ -143,4 +163,17 @@ describe('queryOptions', () => {
const data = queryClient.getQueryData(options.queryKey)
expectTypeOf(data).toEqualTypeOf<unknown>()
})

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<Array<QueryObserverResult>>
>()
})
})

0 comments on commit 837bb0e

Please sign in to comment.