Skip to content

feat(queries): infer types correctly when using useQueries #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 11, 2021
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
4 changes: 2 additions & 2 deletions src/queries/Queries.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import type { UseQueryOptions, UseQueryResult } from '../types'
import useQueries from './useQueries'

export let queries: UseQueryOptions[]
export let queries: readonly UseQueryOptions[]
// useful for binding
export let currentResult: UseQueryResult[]
export let currentResult: readonly UseQueryResult[] = []

let firstRender = true

Expand Down
27 changes: 21 additions & 6 deletions src/queries/useQueries.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import { readable } from 'svelte/store';

import { notifyManager, QueriesObserver, QueryClient } from "../queryCore/core";
import { notifyManager, QueriesObserver, QueryClient, QueryKey } from "../queryCore/core";
import { useQueryClient } from "../queryClientProvider";
import type { UseQueryOptions } from "../types";
import type { UseQueryOptions, UseQueriesStoreResult } from "../types";

export default function useQueries(
queries: UseQueryOptions[]
) {
export default function useQueries<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>(queries: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>[]): UseQueriesStoreResult<UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>[]>;
export default function useQueries<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>(queries: []): UseQueriesStoreResult<UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>[]>;
export default function useQueries<
T extends readonly [...UseQueryOptions[]]
>(queries: T): UseQueriesStoreResult<T>;
export default function useQueries<
T extends readonly [...UseQueryOptions[]]
>(queries: T): UseQueriesStoreResult<T> {
const client: QueryClient = useQueryClient();
const observer = new QueriesObserver(client, queries);

const { subscribe } = readable(observer.getCurrentResult(), (set) => {
return observer.subscribe(notifyManager.batchCalls(set));
});

const setQueries = (newQueries: UseQueryOptions[]) => {
const setQueries = (newQueries: T) => {
if (observer.hasListeners()) {
observer.setQueries(newQueries)
}
Expand Down
26 changes: 13 additions & 13 deletions src/queryCore/core/queriesObserver.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { difference, replaceAt } from './utils'
import { notifyManager } from './notifyManager'
import type { QueryObserverOptions, QueryObserverResult } from './types'
import type { QueryObserverOptions, QueryObserverResult, QueriesObserverResult } from './types'
import type { QueryClient } from './queryClient'
import { NotifyOptions, QueryObserver } from './queryObserver'
import { Subscribable } from './subscribable'

type QueriesObserverListener = (result: QueryObserverResult[]) => void
type QueriesObserverListener<T extends readonly [...QueryObserverOptions[]]> = (result: QueriesObserverResult<T>) => void

export class QueriesObserver extends Subscribable<QueriesObserverListener> {
export class QueriesObserver<T extends readonly [...QueryObserverOptions[]]> extends Subscribable<QueriesObserverListener<T>> {
private client: QueryClient
private result: QueryObserverResult[]
private queries: QueryObserverOptions[]
private result: QueriesObserverResult<T>
private queries: T
private observers: QueryObserver[]
private observersMap: Record<string, QueryObserver>

constructor(client: QueryClient, queries?: QueryObserverOptions[]) {
constructor(client: QueryClient, queries?: T) {
super()

this.client = client
this.queries = []
this.result = []
this.queries = [] as any
this.result = [] as any
this.observers = []
this.observersMap = {}

Expand Down Expand Up @@ -52,24 +52,24 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
}

setQueries(
queries: QueryObserverOptions[],
queries: T,
notifyOptions?: NotifyOptions
): void {
this.queries = queries
this.updateObservers(notifyOptions)
}

getCurrentResult(): QueryObserverResult[] {
getCurrentResult(): QueriesObserverResult<T> {
return this.result
}

getOptimisticResult(queries: QueryObserverOptions[]): QueryObserverResult[] {
getOptimisticResult(queries: T): QueriesObserverResult<T> {
return queries.map(options => {
const defaultedOptions = this.client.defaultQueryObserverOptions(options)
return this.getObserver(defaultedOptions).getOptimisticResult(
defaultedOptions
)
})
}) as any
}

private getObserver(options: QueryObserverOptions): QueryObserver {
Expand Down Expand Up @@ -117,7 +117,7 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {

this.observers = newObservers
this.observersMap = newObserversMap
this.result = newResult
this.result = newResult as any

if (!this.hasListeners()) {
return
Expand Down
12 changes: 12 additions & 0 deletions src/queryCore/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,18 @@ export type InfiniteQueryObserverResult<TData = unknown, TError = unknown> =
| InfiniteQueryObserverRefetchErrorResult<TData, TError>
| InfiniteQueryObserverSuccessResult<TData, TError>

export type QueryObserverOptionsToQueryObserverResult<T extends QueryObserverOptions> =
T extends QueryObserverOptions<any, infer TError> & { queryFn: (...args: any[]) => infer TData | Promise<infer TData> }
? QueryObserverResult<TData, TError>
: T extends QueryObserverOptions<any, infer TError, infer TData, any, any>
? QueryObserverResult<TData, TError>
: never


export type QueriesObserverResult<T extends readonly [...QueryObserverOptions[]]> = {
[K in keyof T]: QueryObserverOptionsToQueryObserverResult<T[K]>
}

export type MutationKey = string | readonly unknown[]

export type MutationStatus = 'idle' | 'loading' | 'success' | 'error'
Expand Down
6 changes: 3 additions & 3 deletions src/queryCore/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ export function difference<T>(array1: T[], array2: T[]): T[] {
return array1.filter(x => array2.indexOf(x) === -1)
}

export function replaceAt<T>(array: T[], index: number, value: T): T[] {
const copy = array.slice(0)
export function replaceAt<T extends readonly V[], V>(array: T, index: number, value: V): T {
const copy = [...array] as const
copy[index] = value
return copy
return copy as T
}

export function timeUntilStale(updatedAt: number, staleTime?: number): number {
Expand Down
9 changes: 8 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type {
QueryObserverResult,
QueryFunction, QueryKey, MutationStatus, MutationKey, MutationFunction,
InfiniteQueryObserverOptions,
InfiniteQueryObserverResult
InfiniteQueryObserverResult,
QueriesObserverResult
} from "./queryCore";
import { RetryDelayValue, RetryValue } from "./queryCore/core/retryer";

Expand Down Expand Up @@ -75,6 +76,12 @@ export interface UseInfiniteQueryOptions<

export type UseInfiniteQueryResult<TData = unknown, TError = unknown> = InfiniteQueryObserverResult<TData, TError>

export interface UseQueriesStoreResult<T extends readonly [...UseQueryOptions[]]> extends Readable<UseQueriesResult<T>> {
setQueries(newQueries: T): void
}

export type UseQueriesResult<T extends readonly [...UseQueryOptions[]]> = QueriesObserverResult<T>


export interface MutationStoreResult<
TData = unknown,
Expand Down
9 changes: 7 additions & 2 deletions storybook/stories/queries/Queries.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@
import { Queries } from '../../../src'
import { useQueries } from '../../../src/queries'

const later = (delay, value) =>
type Later = <T>(delay: number, value: T) => Promise<T>

const later: Later = (delay, value) =>
new Promise(resolve => setTimeout(resolve, delay, value))

// the query fn
const queryFn = () => later(500, 'My Data')
// the query fn 2
const queryFn2 = () => later(500, 'My Data 2')
// the query fn 3
const queryFn3 = () => later(500, true)

const queries = [
{ queryKey: 'myQuery', queryFn },
{ queryKey: 'myQuery2', queryFn: queryFn2 },
]
{ queryKey: 'myQuery3', queryFn: queryFn3 },
] as const

const queriesStore = useQueries(queries)
</script>
Expand Down