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
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[]> = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to use unknown[] here because after ensureArray, TQueryKey is no longer true, as that contains strings as well. I think it's not a big deal, but if you know how to make ensureArray return TQueryKey if the input is an array and [string] is not, we could improve this further.

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 @@ -417,7 +417,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 @@ -448,7 +448,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 @@ -458,7 +472,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 @@ -471,8 +491,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