Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,183 @@ import { describe, expectTypeOf, it } from 'vitest'
import { queryKey, sleep } from '@tanstack/query-test-utils'
import { injectQuery, queryOptions } from '..'
import type { Signal } from '@angular/core'
import type { CreateQueryOptions, OmitKeyof, QueryFunction } from '..'

describe('injectQuery', () => {
it('should return the correct types', () => {
const key = queryKey()
// unspecified query function should default to unknown
const noQueryFn = injectQuery(() => ({
queryKey: key,
}))
expectTypeOf(noQueryFn.data()).toEqualTypeOf<unknown>()
expectTypeOf(noQueryFn.error()).toEqualTypeOf<Error | null>()

// it should infer the result type from the query function
const fromQueryFn = injectQuery(() => ({
queryKey: key,
queryFn: () => 'test',
}))
expectTypeOf(fromQueryFn.data()).toEqualTypeOf<string | undefined>()
expectTypeOf(fromQueryFn.error()).toEqualTypeOf<Error | null>()

// it should be possible to specify the result type
const withResult = injectQuery<string>(() => ({
queryKey: key,
queryFn: () => 'test',
}))
expectTypeOf(withResult.data()).toEqualTypeOf<string | undefined>()
expectTypeOf(withResult.error()).toEqualTypeOf<Error | null>()

// it should be possible to specify the error type
type CustomErrorType = { message: string }
const withError = injectQuery<string, CustomErrorType>(() => ({
queryKey: key,
queryFn: () => 'test',
}))
expectTypeOf(withError.data()).toEqualTypeOf<string | undefined>()
expectTypeOf(withError.error()).toEqualTypeOf<CustomErrorType | null>()

// it should infer the result type from the configuration
const withResultInfer = injectQuery(() => ({
queryKey: key,
queryFn: () => true,
}))
expectTypeOf(withResultInfer.data()).toEqualTypeOf<boolean | undefined>()
expectTypeOf(withResultInfer.error()).toEqualTypeOf<Error | null>()

// it should be possible to specify a union type as result type
const unionTypeSync = injectQuery(() => ({
queryKey: key,
queryFn: () => (Math.random() > 0.5 ? ('a' as const) : ('b' as const)),
}))
expectTypeOf(unionTypeSync.data()).toEqualTypeOf<'a' | 'b' | undefined>()
const unionTypeAsync = injectQuery<'a' | 'b'>(() => ({
queryKey: key,
queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'),
}))
expectTypeOf(unionTypeAsync.data()).toEqualTypeOf<'a' | 'b' | undefined>()

// it should error when the query function result does not match with the specified type
// @ts-expect-error
injectQuery<number>(() => ({ queryKey: key, queryFn: () => 'test' }))

// it should infer the result type from a generic query function
/**
*
*/
function queryFn<T = string>(): Promise<T> {
return Promise.resolve({} as T)
}

const fromGenericQueryFn = injectQuery(() => ({
queryKey: key,
queryFn: () => queryFn(),
}))
expectTypeOf(fromGenericQueryFn.data()).toEqualTypeOf<string | undefined>()
expectTypeOf(fromGenericQueryFn.error()).toEqualTypeOf<Error | null>()

// todo use query options?
const fromGenericOptionsQueryFn = injectQuery(() => ({
queryKey: key,
queryFn: () => queryFn(),
}))
expectTypeOf(fromGenericOptionsQueryFn.data()).toEqualTypeOf<
string | undefined
>()
expectTypeOf(
fromGenericOptionsQueryFn.error(),
).toEqualTypeOf<Error | null>()

type MyData = number
type MyQueryKey = readonly ['my-data', number]

const getMyDataArrayKey: QueryFunction<MyData, MyQueryKey> = ({
queryKey: [, n],
}) => {
return n + 42
}

const fromMyDataArrayKeyQueryFn = injectQuery(() => ({
queryKey: ['my-data', 100] as const,
queryFn: getMyDataArrayKey,
}))
expectTypeOf(fromMyDataArrayKeyQueryFn.data()).toEqualTypeOf<
number | undefined
>()

// it should handle query-functions that return Promise<any>
const fromPromiseAnyQueryFn = injectQuery(() => ({
queryKey: key,
queryFn: () => fetch('return Promise<any>').then((resp) => resp.json()),
}))
expectTypeOf(fromPromiseAnyQueryFn.data()).toEqualTypeOf<any | undefined>()

const getMyDataStringKey: QueryFunction<MyData, ['1']> = (context) => {
expectTypeOf(context.queryKey).toEqualTypeOf<['1']>()
return Number(context.queryKey[0]) + 42
}

const fromGetMyDataStringKeyQueryFn = injectQuery(() => ({
queryKey: ['1'] as ['1'],
queryFn: getMyDataStringKey,
}))
expectTypeOf(fromGetMyDataStringKeyQueryFn.data()).toEqualTypeOf<
number | undefined
>()

// handles wrapped queries with custom fetcher passed as inline queryFn
const createWrappedQuery = <
TQueryKey extends [string, Record<string, unknown>?],
TQueryFnData,
TError,
TData = TQueryFnData,
>(
qk: TQueryKey,
fetcher: (
obj: TQueryKey[1],
token: string,
// return type must be wrapped with TQueryFnReturn
) => Promise<TQueryFnData>,
options?: OmitKeyof<
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn' | 'initialData',
'safely'
>,
) =>
injectQuery(() => ({
queryKey: qk,
queryFn: () => fetcher(qk[1], 'token'),
...options,
}))
const fromWrappedQuery = createWrappedQuery([''], () =>
Promise.resolve('1'),
)
expectTypeOf(fromWrappedQuery.data()).toEqualTypeOf<string | undefined>()

// handles wrapped queries with custom fetcher passed directly to createQuery
const createWrappedFuncStyleQuery = <
TQueryKey extends [string, Record<string, unknown>?],
TQueryFnData,
TError,
TData = TQueryFnData,
>(
qk: TQueryKey,
fetcher: () => Promise<TQueryFnData>,
options?: OmitKeyof<
CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn' | 'initialData',
'safely'
>,
) => injectQuery(() => ({ queryKey: qk, queryFn: fetcher, ...options }))
const fromWrappedFuncStyleQuery = createWrappedFuncStyleQuery([''], () =>
Promise.resolve(true),
)
expectTypeOf(fromWrappedFuncStyleQuery.data()).toEqualTypeOf<
boolean | undefined
>()
})

describe('initialData', () => {
describe('Config object overload', () => {
it('TData should always be defined when initialData is provided as an object', () => {
Expand Down
Loading
Loading