Files
upstream-tanstack-query/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts
upstream-mirror 3a013ccdfe
Some checks failed
autofix.ci / autofix (push) Failing after 4s
Release / Release (push) Has been skipped
GitHub Actions Security Analysis / Run zizmor (push) Failing after 2s
snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:13:08 +05:00

358 lines
11 KiB
TypeScript

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>()
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', () => {
const key = queryKey()
const { data } = injectQuery(() => ({
queryKey: key,
queryFn: () => ({ wow: true }),
initialData: { wow: true },
}))
expectTypeOf(data).toEqualTypeOf<Signal<{ wow: boolean }>>()
})
it('TData should be defined when passed through queryOptions', () => {
const key = queryKey()
const options = () =>
queryOptions({
queryKey: key,
queryFn: () => {
return {
wow: true,
}
},
initialData: {
wow: true,
},
})
const { data } = injectQuery(options)
expectTypeOf(data).toEqualTypeOf<Signal<{ wow: boolean }>>()
})
it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into useQuery', () => {
const key = queryKey()
const options = queryOptions({
queryKey: key,
queryFn: () => Promise.resolve(1),
})
const query = injectQuery(() => ({
...options,
select: (data) => data > 1,
}))
expectTypeOf(query.data).toEqualTypeOf<Signal<boolean | undefined>>()
})
it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => {
const key = queryKey()
const { data } = injectQuery(() => ({
queryKey: key,
queryFn: () => {
return {
wow: true,
}
},
initialData: () => ({
wow: true,
}),
}))
expectTypeOf(data).toEqualTypeOf<Signal<{ wow: boolean }>>()
})
it('TData should have undefined in the union when initialData is NOT provided', () => {
const key = queryKey()
const { data } = injectQuery(() => ({
queryKey: key,
queryFn: () => {
return {
wow: true,
}
},
}))
expectTypeOf(data).toEqualTypeOf<Signal<{ wow: boolean } | undefined>>()
})
it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => {
const key = queryKey()
const { data } = injectQuery(() => ({
queryKey: key,
queryFn: () => {
return {
wow: true,
}
},
initialData: () => undefined as { wow: boolean } | undefined,
}))
expectTypeOf(data).toEqualTypeOf<Signal<{ wow: boolean } | undefined>>()
})
it('TData should be narrowed after an isSuccess check when initialData is provided as a function which can return undefined', () => {
const key = queryKey()
const query = injectQuery(() => ({
queryKey: key,
queryFn: () => {
return {
wow: true,
}
},
initialData: () => undefined as { wow: boolean } | undefined,
}))
if (query.isSuccess()) {
expectTypeOf(query.data).toEqualTypeOf<Signal<{ wow: boolean }>>()
}
})
})
describe('structuralSharing', () => {
it('should be able to use structuralSharing with unknown types', () => {
const key = queryKey()
// https://github.com/TanStack/query/issues/6525#issuecomment-1938411343
injectQuery(() => ({
queryKey: key,
queryFn: () => 5,
structuralSharing: (oldData, newData) => {
expectTypeOf(oldData).toBeUnknown()
expectTypeOf(newData).toBeUnknown()
return newData
},
}))
})
})
})
describe('Discriminated union return type', () => {
it('data should be possibly undefined by default', () => {
const key = queryKey()
const query = injectQuery(() => ({
queryKey: key,
queryFn: () => sleep(0).then(() => 'Some data'),
}))
expectTypeOf(query.data).toEqualTypeOf<Signal<string | undefined>>()
})
it('data should be defined when query is success', () => {
const key = queryKey()
const query = injectQuery(() => ({
queryKey: key,
queryFn: () => sleep(0).then(() => 'Some data'),
}))
if (query.isSuccess()) {
expectTypeOf(query.data).toEqualTypeOf<Signal<string>>()
}
})
it('error should be null when query is success', () => {
const key = queryKey()
const query = injectQuery(() => ({
queryKey: key,
queryFn: () => sleep(0).then(() => 'Some data'),
}))
if (query.isSuccess()) {
expectTypeOf(query.error).toEqualTypeOf<Signal<null>>()
}
})
it('data should be undefined when query is pending', () => {
const key = queryKey()
const query = injectQuery(() => ({
queryKey: key,
queryFn: () => sleep(0).then(() => 'Some data'),
}))
if (query.isPending()) {
expectTypeOf(query.data).toEqualTypeOf<Signal<undefined>>()
}
})
it('error should be defined when query is error', () => {
const key = queryKey()
const query = injectQuery(() => ({
queryKey: key,
queryFn: () => sleep(0).then(() => 'Some data'),
}))
if (query.isError()) {
expectTypeOf(query.error).toEqualTypeOf<Signal<Error>>()
}
})
})
})