Skip to content

Commit

Permalink
feat(types): Include optional TError on DataTag (#8361)
Browse files Browse the repository at this point in the history
* Include TError on DataTag so that getQueryState can infer the error type correctly

* ci: apply automated fixes

* Add type case to QueryFilters tests

* Remove redudant comment

* TInferredError

* Fix Default TError in query filters

* Move to UnsetMarker and add type changes to a few missed datatag usages

* Fixed wasting Dominik's time

* Force build

* Undo

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
Nick-Lucas and autofix-ci[bot] authored Nov 29, 2024
1 parent f245792 commit 0fa1f01
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 20 deletions.
30 changes: 30 additions & 0 deletions packages/query-core/src/__tests__/queryClient.test-d.tsx
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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<QueryState<unknown, Error> | undefined>()
})

it('should be typed if key is tagged', () => {
const queryKey = ['key'] as DataTag<Array<string>, number>
const queryClient = new QueryClient()
const data = queryClient.getQueryState(queryKey)

expectTypeOf(data).toEqualTypeOf<QueryState<number, Error> | undefined>()
})

it('should be typed including error if key is tagged', () => {
type CustomError = Error & { customError: string }
const queryKey = ['key'] as DataTag<Array<string>, number, CustomError>
const queryClient = new QueryClient()
const data = queryClient.getQueryState(queryKey)

expectTypeOf(data).toEqualTypeOf<
QueryState<number, CustomError> | undefined
>()
})
})

describe('fetchInfiniteQuery', () => {
it('should allow passing pages', async () => {
const data = await new QueryClient().fetchInfiniteQuery({
Expand Down
26 changes: 23 additions & 3 deletions packages/query-core/src/__tests__/utils.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<TData, TError> = {
const a: QueryFilters<TData> = {
predicate(query) {
expectTypeOf(query.setData({ a: 1, b: '1' })).toEqualTypeOf<TData>()
return true
Expand All @@ -22,7 +21,28 @@ describe('QueryFilters', () => {
expectTypeOf(data).toEqualTypeOf<TData | undefined>()

const error = queryClient.getQueryState(a.queryKey!)?.error
expectTypeOf(error).toEqualTypeOf<Error | null | undefined>() // maybe one day this can return TError
expectTypeOf(error).toEqualTypeOf<Error | null | undefined>()
})

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<TData, TError> = {
predicate(query) {
expectTypeOf(query.setData({ a: 1, b: '1' })).toEqualTypeOf<TData>()
return true
},
queryKey: ['key'] as DataTag<undefined, TData, TError>,
}

const queryClient = new QueryClient()

const data = queryClient.getQueryData(a.queryKey!)
expectTypeOf(data).toEqualTypeOf<TData | undefined>()

const error = queryClient.getQueryState(a.queryKey!)?.error
expectTypeOf(error).toEqualTypeOf<TError | null | undefined>()
})

it('should be loose typed if generics are defaults', () => {
Expand Down
26 changes: 20 additions & 6 deletions packages/query-core/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {
RefetchQueryFilters,
ResetOptions,
SetDataOptions,
UnsetMarker,
} from './types'
import type { QueryState } from './query'
import type { MutationFilters, QueryFilters, Updater } from './utils'
Expand Down Expand Up @@ -119,7 +120,8 @@ export class QueryClient {
TTaggedQueryKey extends QueryKey = QueryKey,
TInferredQueryFnData = TTaggedQueryKey extends DataTag<
unknown,
infer TaggedValue
infer TaggedValue,
unknown
>
? TaggedValue
: TQueryFnData,
Expand Down Expand Up @@ -169,7 +171,8 @@ export class QueryClient {
TTaggedQueryKey extends QueryKey = QueryKey,
TInferredQueryFnData = TTaggedQueryKey extends DataTag<
unknown,
infer TaggedValue
infer TaggedValue,
unknown
>
? TaggedValue
: TQueryFnData,
Expand Down Expand Up @@ -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<TInferredQueryFnData, TError> | undefined {
): QueryState<TInferredQueryFnData, TInferredError> | undefined {
const options = this.defaultQueryOptions({ queryKey })
return this.#queryCache.get<TInferredQueryFnData, TError>(options.queryHash)
?.state
return this.#queryCache.get<TInferredQueryFnData, TInferredError>(
options.queryHash,
)?.state
}

removeQueries(filters?: QueryFilters): void {
Expand Down
10 changes: 7 additions & 3 deletions packages/query-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ export type DefaultError = Register extends {
export type QueryKey = ReadonlyArray<unknown>

export declare const dataTagSymbol: unique symbol
export type DataTag<TType, TValue> = TType & {
export declare const dataTagErrorSymbol: unique symbol
export declare const unsetMarker: unique symbol
export type UnsetMarker = typeof unsetMarker
export type DataTag<TType, TValue, TError = UnsetMarker> = TType & {
[dataTagSymbol]: TValue
[dataTagErrorSymbol]: TError
}

export type QueryFunction<
Expand Down Expand Up @@ -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<TQueryFnData, TError, TData, TQueryKey> {
Expand All @@ -545,7 +549,7 @@ export interface InvalidateQueryFilters<

export interface RefetchQueryFilters<
TQueryFnData = unknown,
TError = Error,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> extends QueryFilters<TQueryFnData, TError, TData, TQueryKey> {}
Expand Down
4 changes: 2 additions & 2 deletions packages/query-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
> {
Expand All @@ -38,7 +38,7 @@ export interface QueryFilters<
*/
queryKey?: unknown extends TQueryFnData
? QueryKey
: QueryKey & DataTag<unknown, TQueryFnData>
: QueryKey & DataTag<unknown, TQueryFnData, TError>
/**
* Include or exclude stale queries
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-query/src/infiniteQueryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function infiniteQueryOptions<
TQueryKey,
TPageParam
> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
}

export function infiniteQueryOptions<
Expand All @@ -89,7 +89,7 @@ export function infiniteQueryOptions<
TQueryKey,
TPageParam
> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>>
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
}

export function infiniteQueryOptions(options: unknown) {
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-query/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class QueryClient extends QC {
getQueryData<TData = unknown, TTaggedQueryKey extends QueryKey = QueryKey>(
queryKey: TTaggedQueryKey,
):
| (TTaggedQueryKey extends DataTag<unknown, infer TaggedValue>
| (TTaggedQueryKey extends DataTag<unknown, infer TaggedValue, unknown>
? TaggedValue
: TData)
| undefined
Expand Down Expand Up @@ -110,7 +110,7 @@ export class QueryClient extends QC {
setQueryData<
TQueryFnData,
TTaggedQueryKey extends QueryKey,
TData = TTaggedQueryKey extends DataTag<unknown, infer TaggedValue>
TData = TTaggedQueryKey extends DataTag<unknown, infer TaggedValue, unknown>
? TaggedValue
: TQueryFnData,
>(
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-query/src/queryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function queryOptions<
>(
options: UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
): UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
queryKey: DataTag<TQueryKey, TQueryFnData>
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
}

export function queryOptions<
Expand All @@ -23,7 +23,7 @@ export function queryOptions<
>(
options: DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
): DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
queryKey: DataTag<TQueryKey, TQueryFnData>
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
}

export function queryOptions(options: unknown) {
Expand Down

0 comments on commit 0fa1f01

Please sign in to comment.