Skip to content

Commit

Permalink
fix(useQuery): let type of queryKey for the QueryContext be inferred …
Browse files Browse the repository at this point in the history
…from the key passed to useQuery (TanStack#2047)

* fix(useQuery): let type of queryKey for the QueryContext be inferred from the key passed to useQuery

* fix(useInfiniteQuery): let type of queryKey for the QueryContext be inferred from the key passed to useInfiniteQuery

* fix(useQuery): pass TQueryKey down to queryObserver

for more type safety and to make queryKeyHashFn generic as well
  • Loading branch information
TkDodo authored Mar 31, 2021
1 parent 9428bfd commit e42cbc3
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 139 deletions.
64 changes: 40 additions & 24 deletions src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ import { Retryer, CancelOptions, isCancelledError } from './retryer'

// TYPES

interface QueryConfig<TQueryFnData, TError, TData> {
interface QueryConfig<
TQueryFnData,
TError,
TData,
TQueryKey extends QueryKey = QueryKey
> {
cache: QueryCache
queryKey: QueryKey
queryKey: TQueryKey
queryHash: string
options?: QueryOptions<TQueryFnData, TError, TData>
defaultOptions?: QueryOptions<TQueryFnData, TError, TData>
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
defaultOptions?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
state?: QueryState<TData, TError>
}

Expand All @@ -46,20 +51,28 @@ export interface QueryState<TData = unknown, TError = unknown> {
status: QueryStatus
}

export interface FetchContext<TQueryFnData, TError, TData> {
export interface FetchContext<
TQueryFnData,
TError,
TData,
TQueryKey extends QueryKey = QueryKey
> {
fetchFn: () => unknown | Promise<unknown>
fetchOptions?: FetchOptions
options: QueryOptions<TQueryFnData, TError, TData>
queryKey: QueryKey
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
queryKey: TQueryKey
state: QueryState<TData, TError>
}

export interface QueryBehavior<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
> {
onFetch: (context: FetchContext<TQueryFnData, TError, TData>) => void
onFetch: (
context: FetchContext<TQueryFnData, TError, TData, TQueryKey>
) => void
}

export interface FetchOptions {
Expand Down Expand Up @@ -128,11 +141,12 @@ export interface SetStateOptions {
export class Query<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
> {
queryKey: QueryKey
queryKey: TQueryKey
queryHash: string
options!: QueryOptions<TQueryFnData, TError, TData>
options!: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
initialState: QueryState<TData, TError>
revertState?: QueryState<TData, TError>
state: QueryState<TData, TError>
Expand All @@ -142,10 +156,10 @@ export class Query<
private promise?: Promise<TData>
private gcTimeout?: number
private retryer?: Retryer<TData, TError>
private observers: QueryObserver<any, any, any, any>[]
private defaultOptions?: QueryOptions<TQueryFnData, TError, TData>
private observers: QueryObserver<any, any, any, any, any>[]
private defaultOptions?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>

constructor(config: QueryConfig<TQueryFnData, TError, TData>) {
constructor(config: QueryConfig<TQueryFnData, TError, TData, TQueryKey>) {
this.defaultOptions = config.defaultOptions
this.setOptions(config.options)
this.observers = []
Expand All @@ -158,7 +172,7 @@ export class Query<
}

private setOptions(
options?: QueryOptions<TQueryFnData, TError, TData>
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
): void {
this.options = { ...this.defaultOptions, ...options }

Expand All @@ -169,7 +183,9 @@ export class Query<
)
}

setDefaultOptions(options: QueryOptions<TQueryFnData, TError, TData>): void {
setDefaultOptions(
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
): void {
this.defaultOptions = options
}

Expand Down Expand Up @@ -290,7 +306,7 @@ export class Query<
this.retryer?.continue()
}

addObserver(observer: QueryObserver<any, any, any, any>): void {
addObserver(observer: QueryObserver<any, any, any, any, any>): void {
if (this.observers.indexOf(observer) === -1) {
this.observers.push(observer)

Expand All @@ -301,7 +317,7 @@ export class Query<
}
}

removeObserver(observer: QueryObserver<any, any, any, any>): void {
removeObserver(observer: QueryObserver<any, any, any, any, any>): void {
if (this.observers.indexOf(observer) !== -1) {
this.observers = this.observers.filter(x => x !== observer)

Expand Down Expand Up @@ -334,7 +350,7 @@ export class Query<
}

fetch(
options?: QueryOptions<TQueryFnData, TError, TData>,
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
fetchOptions?: FetchOptions
): Promise<TData> {
if (this.state.isFetching) {
Expand Down Expand Up @@ -363,7 +379,7 @@ export class Query<

// Create query function context
const queryKey = ensureArray(this.queryKey)
const queryFnContext: QueryFunctionContext = {
const queryFnContext: QueryFunctionContext<unknown[]> = {
queryKey,
pageParam: undefined,
}
Expand All @@ -375,7 +391,7 @@ export class Query<
: Promise.reject('Missing queryFn')

// Trigger behavior hook
const context: FetchContext<TQueryFnData, TError, TData> = {
const context: FetchContext<TQueryFnData, TError, TData, any> = {
fetchOptions,
options: this.options,
queryKey,
Expand Down Expand Up @@ -421,7 +437,7 @@ export class Query<
if (!isCancelledError(error)) {
// Notify cache callback
if (this.cache.config.onError) {
this.cache.config.onError(error, this as Query)
this.cache.config.onError(error, this as Query<any, any, any, any>)
}

// Log error
Expand Down Expand Up @@ -464,7 +480,7 @@ export class Query<
}

protected getDefaultState(
options: QueryOptions<TQueryFnData, TError, TData>
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
): QueryState<TData, TError> {
const data =
typeof options.initialData === 'function'
Expand Down
41 changes: 23 additions & 18 deletions src/core/queryCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,40 @@ interface QueryCacheConfig {
}

interface QueryHashMap {
[hash: string]: Query<any, any>
[hash: string]: Query<any, any, any, any>
}

interface NotifyEventQueryAdded {
type: 'queryAdded'
query: Query<any, any>
query: Query<any, any, any, any>
}

interface NotifyEventQueryRemoved {
type: 'queryRemoved'
query: Query<any, any>
query: Query<any, any, any, any>
}

interface NotifyEventQueryUpdated {
type: 'queryUpdated'
query: Query<any, any>
query: Query<any, any, any, any>
action: Action<any, any>
}

interface NotifyEventObserverAdded {
type: 'observerAdded'
query: Query<any, any>
observer: QueryObserver<any, any, any, any>
query: Query<any, any, any, any>
observer: QueryObserver<any, any, any, any, any>
}

interface NotifyEventObserverRemoved {
type: 'observerRemoved'
query: Query<any, any>
observer: QueryObserver<any, any, any, any>
query: Query<any, any, any, any>
observer: QueryObserver<any, any, any, any, any>
}

interface NotifyEventObserverResultsUpdated {
type: 'observerResultsUpdated'
query: Query<any, any>
query: Query<any, any, any, any>
}

type QueryCacheNotifyEvent =
Expand All @@ -69,7 +69,7 @@ type QueryCacheListener = (event?: QueryCacheNotifyEvent) => void
export class QueryCache extends Subscribable<QueryCacheListener> {
config: QueryCacheConfig

private queries: Query<any, any>[]
private queries: Query<any, any, any, any>[]
private queriesMap: QueryHashMap

constructor(config?: QueryCacheConfig) {
Expand All @@ -79,15 +79,15 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
this.queriesMap = {}
}

build<TQueryFnData, TError, TData>(
build<TQueryFnData, TError, TData, TQueryKey extends QueryKey>(
client: QueryClient,
options: QueryOptions<TQueryFnData, TError, TData>,
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
state?: QueryState<TData, TError>
): Query<TQueryFnData, TError, TData> {
): Query<TQueryFnData, TError, TData, TQueryKey> {
const queryKey = options.queryKey!
const queryHash =
options.queryHash ?? hashQueryKeyByOptions(queryKey, options)
let query = this.get<TQueryFnData, TError, TData>(queryHash)
let query = this.get<TQueryFnData, TError, TData, TQueryKey>(queryHash)

if (!query) {
query = new Query({
Expand All @@ -104,7 +104,7 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
return query
}

add(query: Query<any, any>): void {
add(query: Query<any, any, any, any>): void {
if (!this.queriesMap[query.queryHash]) {
this.queriesMap[query.queryHash] = query
this.queries.push(query)
Expand All @@ -115,7 +115,7 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
}
}

remove(query: Query<any, any>): void {
remove(query: Query<any, any, any, any>): void {
const queryInMap = this.queriesMap[query.queryHash]

if (queryInMap) {
Expand All @@ -139,9 +139,14 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
})
}

get<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData>(
get<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueyKey extends QueryKey = QueryKey
>(
queryHash: string
): Query<TQueryFnData, TError, TData> | undefined {
): Query<TQueryFnData, TError, TData, TQueyKey> | undefined {
return this.queriesMap[queryHash]
}

Expand Down
42 changes: 37 additions & 5 deletions src/core/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ export class QueryClient {

getQueryDefaults(
queryKey?: QueryKey
): QueryObserverOptions<any, any, any, any> | undefined {
): QueryObserverOptions<any, any, any, any, any> | undefined {
return queryKey
? this.queryDefaults.find(x => partialMatchKey(queryKey, x.queryKey))
?.defaultOptions
Expand Down Expand Up @@ -453,7 +453,21 @@ export class QueryClient {
: undefined
}

defaultQueryOptions<T extends QueryOptions<any, any, any>>(options?: T): T {
defaultQueryOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey extends QueryKey
>(
options?: QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>
): QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> {
if (options?._defaulted) {
return options
}
Expand All @@ -463,7 +477,13 @@ export class QueryClient {
...this.getQueryDefaults(options?.queryKey),
...options,
_defaulted: true,
} as T
} as QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>

if (!defaultedOptions.queryHash && defaultedOptions.queryKey) {
defaultedOptions.queryHash = hashQueryKeyByOptions(
Expand All @@ -476,8 +496,20 @@ export class QueryClient {
}

defaultQueryObserverOptions<
T extends QueryObserverOptions<any, any, any, any>
>(options?: T): T {
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey extends QueryKey
>(
options?: QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>
): QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> {
return this.defaultQueryOptions(options)
}

Expand Down
Loading

0 comments on commit e42cbc3

Please sign in to comment.