diff --git a/packages/query-core/src/__tests__/queryClient.test-d.tsx b/packages/query-core/src/__tests__/queryClient.test-d.tsx index 2ec94d4450..8ff728c3db 100644 --- a/packages/query-core/src/__tests__/queryClient.test-d.tsx +++ b/packages/query-core/src/__tests__/queryClient.test-d.tsx @@ -1,5 +1,6 @@ import { describe, expectTypeOf, it } from 'vitest' import { QueryClient } from '../queryClient' +import type { QueryState } from '../query' import type { DataTag, InfiniteData, QueryKey } from '../types' describe('getQueryData', () => { @@ -101,6 +102,35 @@ describe('setQueryData', () => { }) }) +describe('getQueryState', () => { + it('should be loose typed without tag', () => { + const queryKey = ['key'] as const + const queryClient = new QueryClient() + const data = queryClient.getQueryState(queryKey) + + expectTypeOf(data).toEqualTypeOf | undefined>() + }) + + it('should be typed if key is tagged', () => { + const queryKey = ['key'] as DataTag, number> + const queryClient = new QueryClient() + const data = queryClient.getQueryState(queryKey) + + expectTypeOf(data).toEqualTypeOf | undefined>() + }) + + it('should be typed including error if key is tagged', () => { + type CustomError = Error & { customError: string } + const queryKey = ['key'] as DataTag, number, CustomError> + const queryClient = new QueryClient() + const data = queryClient.getQueryState(queryKey) + + expectTypeOf(data).toEqualTypeOf< + QueryState | undefined + >() + }) +}) + describe('fetchInfiniteQuery', () => { it('should allow passing pages', async () => { const data = await new QueryClient().fetchInfiniteQuery({ diff --git a/packages/query-core/src/__tests__/utils.test-d.tsx b/packages/query-core/src/__tests__/utils.test-d.tsx index f2ac65bfcc..7a694453a7 100644 --- a/packages/query-core/src/__tests__/utils.test-d.tsx +++ b/packages/query-core/src/__tests__/utils.test-d.tsx @@ -6,9 +6,8 @@ import type { DataTag } from '../types' describe('QueryFilters', () => { it('should be typed if generics are passed', () => { type TData = { a: number; b: string } - type TError = { message: string } - const a: QueryFilters = { + const a: QueryFilters = { predicate(query) { expectTypeOf(query.setData({ a: 1, b: '1' })).toEqualTypeOf() return true @@ -22,7 +21,28 @@ describe('QueryFilters', () => { expectTypeOf(data).toEqualTypeOf() const error = queryClient.getQueryState(a.queryKey!)?.error - expectTypeOf(error).toEqualTypeOf() // maybe one day this can return TError + expectTypeOf(error).toEqualTypeOf() + }) + + it('should be typed if generics are passed including an error type', () => { + type TData = { a: number; b: string } + type TError = Error & { message: string } + + const a: QueryFilters = { + predicate(query) { + expectTypeOf(query.setData({ a: 1, b: '1' })).toEqualTypeOf() + return true + }, + queryKey: ['key'] as DataTag, + } + + const queryClient = new QueryClient() + + const data = queryClient.getQueryData(a.queryKey!) + expectTypeOf(data).toEqualTypeOf() + + const error = queryClient.getQueryState(a.queryKey!)?.error + expectTypeOf(error).toEqualTypeOf() }) it('should be loose typed if generics are defaults', () => { diff --git a/packages/query-core/src/queryClient.ts b/packages/query-core/src/queryClient.ts index 49032a8802..04bc37ab57 100644 --- a/packages/query-core/src/queryClient.ts +++ b/packages/query-core/src/queryClient.ts @@ -39,6 +39,7 @@ import type { RefetchQueryFilters, ResetOptions, SetDataOptions, + UnsetMarker, } from './types' import type { QueryState } from './query' import type { MutationFilters, QueryFilters, Updater } from './utils' @@ -119,7 +120,8 @@ export class QueryClient { TTaggedQueryKey extends QueryKey = QueryKey, TInferredQueryFnData = TTaggedQueryKey extends DataTag< unknown, - infer TaggedValue + infer TaggedValue, + unknown > ? TaggedValue : TQueryFnData, @@ -169,7 +171,8 @@ export class QueryClient { TTaggedQueryKey extends QueryKey = QueryKey, TInferredQueryFnData = TTaggedQueryKey extends DataTag< unknown, - infer TaggedValue + infer TaggedValue, + unknown > ? TaggedValue : TQueryFnData, @@ -225,16 +228,27 @@ export class QueryClient { TTaggedQueryKey extends QueryKey = QueryKey, TInferredQueryFnData = TTaggedQueryKey extends DataTag< unknown, - infer TaggedValue + infer TaggedValue, + unknown > ? TaggedValue : TQueryFnData, + TInferredError = TTaggedQueryKey extends DataTag< + unknown, + unknown, + infer TaggedError + > + ? TaggedError extends UnsetMarker + ? TError + : TaggedError + : TError, >( queryKey: TTaggedQueryKey, - ): QueryState | undefined { + ): QueryState | undefined { const options = this.defaultQueryOptions({ queryKey }) - return this.#queryCache.get(options.queryHash) - ?.state + return this.#queryCache.get( + options.queryHash, + )?.state } removeQueries(filters?: QueryFilters): void { diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index bbf413d1e9..a21016bf2c 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -43,8 +43,12 @@ export type DefaultError = Register extends { export type QueryKey = ReadonlyArray export declare const dataTagSymbol: unique symbol -export type DataTag = TType & { +export declare const dataTagErrorSymbol: unique symbol +export declare const unsetMarker: unique symbol +export type UnsetMarker = typeof unsetMarker +export type DataTag = TType & { [dataTagSymbol]: TValue + [dataTagErrorSymbol]: TError } export type QueryFunction< @@ -536,7 +540,7 @@ export interface RefetchOptions extends ResultOptions { export interface InvalidateQueryFilters< TQueryFnData = unknown, - TError = Error, + TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends QueryFilters { @@ -545,7 +549,7 @@ export interface InvalidateQueryFilters< export interface RefetchQueryFilters< TQueryFnData = unknown, - TError = Error, + TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > extends QueryFilters {} diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 07c134e4ae..c549a311b7 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -17,7 +17,7 @@ import type { FetchOptions, Query } from './query' export interface QueryFilters< TQueryFnData = unknown, - TError = Error, + TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > { @@ -38,7 +38,7 @@ export interface QueryFilters< */ queryKey?: unknown extends TQueryFnData ? QueryKey - : QueryKey & DataTag + : QueryKey & DataTag /** * Include or exclude stale queries */ diff --git a/packages/vue-query/src/infiniteQueryOptions.ts b/packages/vue-query/src/infiniteQueryOptions.ts index 3069219a10..dbe296e1af 100644 --- a/packages/vue-query/src/infiniteQueryOptions.ts +++ b/packages/vue-query/src/infiniteQueryOptions.ts @@ -65,7 +65,7 @@ export function infiniteQueryOptions< TQueryKey, TPageParam > & { - queryKey: DataTag> + queryKey: DataTag, TError> } export function infiniteQueryOptions< @@ -89,7 +89,7 @@ export function infiniteQueryOptions< TQueryKey, TPageParam > & { - queryKey: DataTag> + queryKey: DataTag, TError> } export function infiniteQueryOptions(options: unknown) { diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index 928dd23aaa..7712268bb7 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -57,7 +57,7 @@ export class QueryClient extends QC { getQueryData( queryKey: TTaggedQueryKey, ): - | (TTaggedQueryKey extends DataTag + | (TTaggedQueryKey extends DataTag ? TaggedValue : TData) | undefined @@ -110,7 +110,7 @@ export class QueryClient extends QC { setQueryData< TQueryFnData, TTaggedQueryKey extends QueryKey, - TData = TTaggedQueryKey extends DataTag + TData = TTaggedQueryKey extends DataTag ? TaggedValue : TQueryFnData, >( diff --git a/packages/vue-query/src/queryOptions.ts b/packages/vue-query/src/queryOptions.ts index 1221222e3c..558756c44f 100644 --- a/packages/vue-query/src/queryOptions.ts +++ b/packages/vue-query/src/queryOptions.ts @@ -12,7 +12,7 @@ export function queryOptions< >( options: UndefinedInitialQueryOptions, ): UndefinedInitialQueryOptions & { - queryKey: DataTag + queryKey: DataTag } export function queryOptions< @@ -23,7 +23,7 @@ export function queryOptions< >( options: DefinedInitialQueryOptions, ): DefinedInitialQueryOptions & { - queryKey: DataTag + queryKey: DataTag } export function queryOptions(options: unknown) {