[react-query] Update API to 1.10.0 (#43438)

* [react-query] Update API to 1.10.0
- breaking changes

* Make use of extra variables
* Fixing lint errors
* Complete useInfiniteQuery
* Add global config
* Add queryCache
* Allow keys and variables with infinite tails
* Add discriminated unions for base and paginated queries
* Discriminated union for mutation result
* Require first key element to be a string subtype
This commit is contained in:
Igor Oleinikov 2020-03-30 15:23:50 -07:00 committed by GitHub
parent 21c3e41669
commit 2ea0ccd01f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 682 additions and 165 deletions

View File

@ -1,32 +1,171 @@
// Type definitions for react-query 0.3
// Type definitions for react-query 1.1
// Project: https://github.com/tannerlinsley/react-query
// Definitions by: Lukasz Fiszer <https://github.com/lukaszfiszer>
// Jace Hensley <https://github.com/jacehensley>
// Matteo Frana <https://github.com/matteofrana>
// Igor Oleinikov <https://github.com/igorbek>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// Minimum TypeScript Version: 3.7
import { ComponentType } from 'react';
import * as React from 'react';
import * as _ from 'ts-toolbelt';
// overloaded useQuery function with pagination
export function useQuery<TResult, TVariables extends object>(
queryKey: QueryKey<TVariables>,
queryFn: QueryFunction<TResult, TVariables>,
options: QueryOptionsPaginated<TResult>
): QueryResultPaginated<TResult, TVariables>;
// overloaded useQuery function
export function useQuery<TResult, TKey extends AnyQueryKey>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: QueryFunction<TResult, TKey>,
config?: QueryOptions<TResult>,
): QueryResult<TResult>;
export function useQuery<TResult, TVariables extends object>(
queryKey: QueryKey<TVariables>,
queryFn: QueryFunction<TResult, TVariables>,
options?: QueryOptions<TResult>
): QueryResult<TResult, TVariables>;
export function useQuery<TResult, TKey extends string>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: QueryFunction<TResult, [TKey]>,
config?: QueryOptions<TResult>,
): QueryResult<TResult>;
export type QueryKey<TVariables> = string | [string, TVariables] | false | null | QueryKeyFunction<TVariables>;
export type QueryKeyFunction<TVariables> = () => string | [string, TVariables] | false | null;
export function useQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: QueryFunctionWithVariables<TResult, TKey, TVariables>,
config?: QueryOptions<TResult>,
): QueryResult<TResult>;
export type QueryFunction<TResult, TVariables extends object> = (variables: TVariables) => Promise<TResult>;
export function useQuery<TResult, TKey extends string, TVariables extends AnyVariables>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: QueryFunctionWithVariables<TResult, [TKey], TVariables>,
config?: QueryOptions<TResult>,
): QueryResult<TResult>;
export interface QueryOptions<TResult> {
export function useQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables = []>({
queryKey,
variables,
queryFn,
config,
}: {
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined);
variables?: TVariables;
queryFn: QueryFunctionWithVariables<TResult, TKey, TVariables>;
config?: QueryOptions<TResult>;
}): QueryResult<TResult>;
// usePaginatedQuery
export function usePaginatedQuery<TResult, TKey extends AnyQueryKey>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: QueryFunction<TResult, TKey>,
config?: QueryOptions<TResult>,
): PaginatedQueryResult<TResult>;
export function usePaginatedQuery<TResult, TKey extends string>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: QueryFunction<TResult, [TKey]>,
config?: QueryOptions<TResult>,
): PaginatedQueryResult<TResult>;
export function usePaginatedQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: QueryFunctionWithVariables<TResult, TKey, TVariables>,
config?: QueryOptions<TResult>,
): PaginatedQueryResult<TResult>;
export function usePaginatedQuery<TResult, TKey extends string, TVariables extends AnyVariables>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: QueryFunctionWithVariables<TResult, [TKey], TVariables>,
config?: QueryOptions<TResult>,
): PaginatedQueryResult<TResult>;
export function usePaginatedQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables = []>({
queryKey,
variables,
queryFn,
config,
}: {
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined);
variables?: TVariables;
queryFn: QueryFunctionWithVariables<TResult, TKey, TVariables>;
config?: QueryOptions<TResult>;
}): PaginatedQueryResult<TResult>;
// useInfiniteQuery
export function useInfiniteQuery<TResult, TKey extends AnyQueryKey, TMoreVariable>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: InfiniteQueryFunction<TResult, TKey, TMoreVariable>,
config?: InfiniteQueryOptions<TResult, TMoreVariable>,
): InfiniteQueryResult<TResult, TMoreVariable>;
export function useInfiniteQuery<TResult, TKey extends string, TMoreVariable>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: InfiniteQueryFunction<TResult, [TKey], TMoreVariable>,
config?: InfiniteQueryOptions<TResult, TMoreVariable>,
): InfiniteQueryResult<TResult, TMoreVariable>;
export function useInfiniteQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables, TMoreVariable>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: InfiniteQueryFunctionWithVariables<TResult, TKey, TVariables, TMoreVariable>,
config?: InfiniteQueryOptions<TResult, TMoreVariable>,
): InfiniteQueryResult<TResult, TMoreVariable>;
export function useInfiniteQuery<TResult, TKey extends string, TVariables extends AnyVariables, TMoreVariable>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: InfiniteQueryFunctionWithVariables<TResult, [TKey], TVariables, TMoreVariable>,
config?: InfiniteQueryOptions<TResult, TMoreVariable>,
): InfiniteQueryResult<TResult, TMoreVariable>;
export function useInfiniteQuery<
TResult,
TKey extends AnyQueryKey,
TMoreVariable,
TVariables extends AnyVariables = []
>({
queryKey,
variables,
queryFn,
config,
}: {
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined);
variables?: TVariables;
queryFn: InfiniteQueryFunctionWithVariables<TResult, TKey, TVariables, TMoreVariable>;
config?: InfiniteQueryOptions<TResult, TMoreVariable>;
}): InfiniteQueryResult<TResult, TMoreVariable>;
export type QueryKeyPart = string | object | boolean | number | null | readonly QueryKeyPart[] | null | undefined;
export type AnyQueryKey = readonly [string, ...QueryKeyPart[]]; // this forces the key to be inferred as a tuple
export type AnyVariables = readonly [] | readonly [any, ...any[]]; // this forces the variables to be inferred as a tuple
export type QueryFunction<TResult, TKey extends AnyQueryKey> = (...key: TKey) => Promise<TResult>;
export type QueryFunctionWithVariables<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables> = (
...key: _.List.Concat<TKey, TVariables>
) => Promise<TResult>;
export type InfiniteQueryFunction<TResult, TKey extends AnyQueryKey, TMoreVariable> = (
...keysAndMore: _.List.Append<TKey, TMoreVariable> | TKey
) => Promise<TResult>;
export type InfiniteQueryFunctionWithVariables<
TResult,
TKey extends AnyQueryKey,
TVariables extends AnyVariables,
TMoreVariable
> = (
...keysAndVariablesAndMore:
| _.List.Append<_.List.Concat<TKey, TVariables>, TMoreVariable>
| _.List.Concat<TKey, TVariables>
) => Promise<TResult>;
export interface BaseQueryOptions {
/**
* Set this to `true` to disable automatic refetching when the query mounts or changes query keys.
* To refetch the query, use the `refetch` method returned from the `useQuery` instance.
*/
manual?: boolean;
/**
* If `false`, failed queries will not retry by default.
* If `true`, failed queries will retry infinitely.
* If set to an integer number, e.g. 3, failed queries will retry until the failed query count meets that number.
*/
retry?: boolean | number;
retryDelay?: (retryAttempt: number) => number;
staleTime?: number;
@ -34,109 +173,248 @@ export interface QueryOptions<TResult> {
refetchInterval?: false | number;
refetchIntervalInBackground?: boolean;
refetchOnWindowFocus?: boolean;
onError?: (err: any) => void;
onSuccess?: (data: TResult) => void;
refetchOnMount?: boolean;
onError?: (err: unknown) => void;
suspense?: boolean;
}
export interface QueryOptions<TResult> extends BaseQueryOptions {
onSuccess?: (data: TResult) => void;
onSettled?: (data: TResult | undefined, error: unknown | null) => void;
initialData?: TResult;
}
export interface QueryOptionsPaginated<TResult> extends QueryOptions<TResult> {
paginated: true;
getCanFetchMore: (lastPage: TResult, allPages: TResult[]) => boolean;
export interface InfiniteQueryOptions<TResult, TMoreVariable> extends QueryOptions<TResult> {
getFetchMore: (lastPage: TResult, allPages: TResult[]) => TMoreVariable | false;
}
export interface QueryResult<TResult, TVariables> {
data: null | TResult;
error: null | Error;
isLoading: boolean;
export interface QueryResultBase<TResult> {
status: 'loading' | 'error' | 'success';
error: null | unknown;
isFetching: boolean;
isCached: boolean;
failureCount: number;
refetch: (arg?: {variables?: TVariables, merge?: (...args: any[]) => any, disableThrow?: boolean}) => Promise<void>;
refetch: ({ force, throwOnError }?: { force?: boolean; throwOnError?: boolean }) => Promise<TResult>;
}
export interface QueryResultPaginated<TResult, TVariables> extends QueryResult<TResult[], TVariables> {
export interface QueryLoadingResult<TResult> extends QueryResultBase<TResult> {
status: 'loading';
data: TResult | undefined; // even when error, data can have stale data
error: unknown | null; // it still can be error
}
export interface QueryErrorResult<TResult> extends QueryResultBase<TResult> {
status: 'error';
data: TResult | undefined; // even when error, data can have stale data
error: unknown;
}
export interface QuerySuccessResult<TResult> extends QueryResultBase<TResult> {
status: 'success';
data: TResult;
error: null;
}
export type QueryResult<TResult> =
| QueryLoadingResult<TResult>
| QueryErrorResult<TResult>
| QuerySuccessResult<TResult>;
export interface PaginatedQueryLoadingResult<TResult> extends QueryResultBase<TResult> {
status: 'loading';
resolvedData: undefined | TResult; // even when error, data can have stale data
latestData: undefined | TResult; // even when error, data can have stale data
error: unknown | null; // it still can be error
}
export interface PaginatedQueryErrorResult<TResult> extends QueryResultBase<TResult> {
status: 'error';
resolvedData: undefined | TResult; // even when error, data can have stale data
latestData: undefined | TResult; // even when error, data can have stale data
error: unknown;
}
export interface PaginatedQuerySuccessResult<TResult> extends QueryResultBase<TResult> {
status: 'success';
resolvedData: TResult;
latestData: TResult;
error: null;
}
export type PaginatedQueryResult<TResult> =
| PaginatedQueryLoadingResult<TResult>
| PaginatedQueryErrorResult<TResult>
| PaginatedQuerySuccessResult<TResult>;
export interface InfiniteQueryResult<TResult, TMoreVariable> extends QueryResultBase<TResult[]> {
data: TResult[];
isFetchingMore: boolean;
canFetchMore: boolean;
fetchMore: (variables?: TVariables) => Promise<TResult>;
fetchMore: (moreVariable?: TMoreVariable | false) => Promise<TResult[]> | undefined;
}
export function prefetchQuery<TResult, TVariables extends object>(
queryKey: QueryKey<TVariables>,
queryFn: QueryFunction<TResult, TVariables>,
options?: PrefetchQueryOptions<TResult>
): Promise<TResult>;
export interface PrefetchQueryOptions<TResult> extends QueryOptions<TResult> {
force?: boolean;
}
export function useMutation<TResults, TVariables extends object>(
export function useMutation<TResults, TVariables = undefined>(
mutationFn: MutationFunction<TResults, TVariables>,
mutationOptions?: MutationOptions
mutationOptions?: MutationOptions<TResults, TVariables>,
): [MutateFunction<TResults, TVariables>, MutationResult<TResults>];
export type MutationFunction<TResults, TVariables extends object> = (
variables: TVariables,
) => Promise<TResults>;
export type MutationFunction<TResults, TVariables> = (variables: TVariables) => Promise<TResults>;
export interface MutationOptions {
refetchQueries?: Array<string | [string, object]>;
refetchQueriesOnFailure?: boolean;
export interface MutateOptions<TResult, TVariables> {
onSuccess?: (data: TResult, variables: TVariables) => Promise<void> | void;
onError?: (error: unknown, variables: TVariables, snapshotValue: unknown) => Promise<void> | void;
onSettled?: (
data: undefined | TResult,
error: unknown | null,
variables: TVariables,
snapshotValue?: unknown,
) => Promise<void> | void;
throwOnError?: boolean;
}
export type MutateFunction<TResults, TVariables extends object> = (
variables?: TVariables,
options?: {
updateQuery?: string | [string, object],
waitForRefetchQueries?: boolean;
}
) => Promise<TResults>;
export interface MutationOptions<TResult, TVariables> extends MutateOptions<TResult, TVariables> {
onMutate?: (variables: TVariables) => Promise<unknown> | unknown;
useErrorBoundary?: boolean;
}
export interface MutationResult<TResults> {
data: TResults | null;
isLoading: boolean;
error: null | Error;
promise: Promise<TResults>;
export type MutateFunction<TResult, TVariables> = undefined extends TVariables
? (variables?: TVariables, options?: MutateOptions<TResult, TVariables>) => Promise<TResult>
: (variables: TVariables, options?: MutateOptions<TResult, TVariables>) => Promise<TResult>;
export interface MutationResultBase<TResult> {
status: 'idle' | 'loading' | 'error' | 'success';
data: undefined | TResult;
error: null | unknown;
promise: Promise<TResult>;
reset: () => void;
}
export function setQueryData(
queryKey: string | [string, object],
data: any,
options?: {
shouldRefetch?: boolean
}
): void | Promise<void>;
export function refetchQuery(
queryKey: string | [string, object] | [string, false],
force?: {
force?: boolean
}
): Promise<void>;
export function refetchAllQueries(
options?: {
force?: boolean,
includeInactive: boolean
}
): Promise<void>;
export function useIsFetching(): boolean;
export const ReactQueryConfigProvider: React.ComponentType<{
config?: ReactQueryProviderConfig
}>;
export interface ReactQueryProviderConfig {
retry?: number;
retryDelay?: (attempt: number) => number;
staleTime?: number;
cacheTime?: number;
refetchAllOnWindowFocus?: boolean;
refetchInterval?: false | number;
suspense?: boolean;
export interface IdleMutationResult<TResult> extends MutationResultBase<TResult> {
status: 'idle';
data: undefined;
error: null;
}
export function clearQueryCache(): void;
export interface LoadingMutationResult<TResult> extends MutationResultBase<TResult> {
status: 'loading';
data: undefined;
error: undefined;
}
export interface ErrorMutationResult<TResult> extends MutationResultBase<TResult> {
status: 'error';
data: undefined;
error: unknown;
}
export interface SuccessMutationResult<TResult> extends MutationResultBase<TResult> {
status: 'success';
data: TResult;
error: undefined;
}
export type MutationResult<TResult> =
| IdleMutationResult<TResult>
| LoadingMutationResult<TResult>
| ErrorMutationResult<TResult>
| SuccessMutationResult<TResult>;
export interface CachedQuery {
queryKey: AnyQueryKey;
queryVariables: AnyVariables;
queryFn: (...args: any[]) => unknown;
config: QueryOptions<unknown>;
state: unknown;
setData(dataOrUpdater: unknown | ((oldData: unknown | undefined) => unknown)): void;
}
export interface QueryCache {
prefetchQuery<TResult, TKey extends AnyQueryKey>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: QueryFunction<TResult, TKey>,
config?: QueryOptions<TResult>,
): Promise<TResult>;
prefetchQuery<TResult, TKey extends string>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
queryFn: QueryFunction<TResult, [TKey]>,
config?: QueryOptions<TResult>,
): Promise<TResult>;
prefetchQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: QueryFunctionWithVariables<TResult, TKey, TVariables>,
config?: QueryOptions<TResult>,
): Promise<TResult>;
prefetchQuery<TResult, TKey extends string, TVariables extends AnyVariables>(
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined),
variables: TVariables,
queryFn: QueryFunctionWithVariables<TResult, [TKey], TVariables>,
config?: QueryOptions<TResult>,
): Promise<TResult>;
prefetchQuery<TResult, TKey extends AnyQueryKey, TVariables extends AnyVariables = []>({
queryKey,
variables,
queryFn,
config,
}: {
queryKey: TKey | false | null | undefined | (() => TKey | false | null | undefined);
variables?: TVariables;
queryFn: QueryFunctionWithVariables<TResult, TKey, TVariables>;
config?: QueryOptions<TResult>;
}): Promise<TResult>;
getQueryData(key: AnyQueryKey | string): unknown | undefined;
setQueryData(key: AnyQueryKey | string, dataOrUpdater: unknown | ((oldData: unknown | undefined) => unknown)): void;
refetchQueries(
queryKeyOrPredicateFn: AnyQueryKey | string | ((query: CachedQuery) => boolean),
{ exact, throwOnError, force }?: { exact?: boolean; throwOnError?: boolean; force?: boolean },
): Promise<void>;
removeQueries(
queryKeyOrPredicateFn: AnyQueryKey | string | ((query: CachedQuery) => boolean),
{ exact }?: { exact?: boolean },
): Promise<void>;
getQuery(queryKey: AnyQueryKey): CachedQuery | undefined;
getQueries(queryKey: AnyQueryKey): CachedQuery[];
isFetching: number;
subscribe(callback: (queryCache: QueryCache) => void): () => void;
clear(): CachedQuery[];
}
export const queryCache: QueryCache;
/**
* A hook that returns the number of the quiries that your application is loading or fetching in the background
* (useful for app-wide loading indicators).
* @returns the number of the quiries that your application is currently loading or fetching in the background.
*/
export function useIsFetching(): number;
export const ReactQueryConfigProvider: React.ComponentType<{
config?: ReactQueryProviderConfig;
}>;
export interface ReactQueryProviderConfig extends BaseQueryOptions {
/** Defaults to the value of `suspense` if not defined otherwise */
useErrorBoundary?: boolean;
throwOnError?: boolean;
refetchAllOnWindowFocus?: boolean;
queryKeySerializerFn?: (
queryKey: QueryKeyPart[] | string | false | undefined | (() => QueryKeyPart[] | string | false | undefined),
) => [string, QueryKeyPart[]] | [];
onMutate?: (variables: unknown) => Promise<unknown> | unknown;
onSuccess?: (data: unknown, variables?: unknown) => void;
onError?: (err: unknown, snapshotValue?: unknown) => void;
onSettled?: (data: unknown | undefined, error: unknown | null, snapshotValue?: unknown) => void;
}
export type ConsoleFunction = (...args: any[]) => void;
export interface ConsoleObject {
log: ConsoleFunction;
warn: ConsoleFunction;
error: ConsoleFunction;
}
export function setConsole(consoleObject: ConsoleObject): void;

View File

@ -0,0 +1,6 @@
{
"private": true,
"dependencies": {
"ts-toolbelt": "*"
}
}

View File

@ -1,83 +1,316 @@
import { useMutation, useQuery } from "react-query";
import {
useMutation,
useQuery,
usePaginatedQuery,
useInfiniteQuery,
useIsFetching,
setConsole,
ReactQueryProviderConfig,
} from 'react-query';
const queryFn = () => Promise.resolve();
function simpleQuery() {
// Query - simple case
const querySimple = useQuery('todos', () => Promise.resolve('test'));
querySimple.data; // $ExpectType string | undefined
querySimple.error; // $ExpectType unknown
querySimple.isFetching; // $ExpectType boolean
querySimple.refetch(); // $ExpectType Promise<string>
querySimple.fetchMore; // $ExpectError
querySimple.canFetchMore; // $ExpectError
querySimple.isFetchingMore; // $ExpectError
}
// Query - simple case
const querySimple = useQuery('todos',
() => Promise.resolve('test'));
querySimple.data; // $ExpectType string | null
querySimple.error; // $ExpectType Error | null
querySimple.isLoading; // $ExpectType boolean
querySimple.refetch(); // $ExpectType Promise<void>
queryPaginated.fetchMore; // $ExpectError
queryPaginated.canFetchMore; // $ExpectError
queryPaginated.isFetchingMore; // $ExpectError
function queryWithVariables() {
// Query Variables
const param = 'test';
const queryVariables = useQuery(['todos', { param }, 10], (key, variables, id) =>
Promise.resolve(variables.param === 'test'),
);
// Query Variables
const param = 'test';
const queryVariables = useQuery(['todos', {param}],
(variables) => Promise.resolve(variables.param === 'test'));
queryVariables.data; // $ExpectType boolean | undefined
queryVariables.refetch(); // $ExpectType Promise<boolean>
queryVariables.refetch({ force: true }); // $ExpectType Promise<boolean>
}
queryVariables.data; // $ExpectType boolean | null
queryVariables.refetch({variables: {param: 'foo'}}); // $ExpectType Promise<void>
queryVariables.refetch({variables: {other: 'foo'}}); // $ExpectError
function invalidSimpleQuery() {
// first element in the key must be a string
useQuery([10, 'a'], async (id, key) => id); // $ExpectError
}
// Query with falsey query key
useQuery(false && ['foo', { bar: 'baz' }], queryFn);
function conditionalQuery(condition: boolean) {
const queryFn1 = (name: string, params: { bar: string }) => Promise.resolve(10);
const queryFn2 = () => Promise.resolve('test');
// Query with query key function
useQuery(() => ['foo', { bar: 'baz' }], queryFn);
// Query with falsey query key
useQuery(condition && ['foo', { bar: 'baz' }], queryFn1);
useQuery(condition && ['foo', { bar: 'baz' }], queryFn2);
useQuery({
queryKey: condition && ['foo', { bar: 'baz' }],
queryFn: queryFn1,
});
// Query with nested variabes
const queryNested = useQuery(['key', {
nested: {
props: [1, 2]
// Query with query key function
useQuery(() => ['foo', { bar: 'baz' }], queryFn1);
useQuery(() => ['foo', { bar: 'baz' }], queryFn2);
}
function queryWithNestedKey() {
// Query with nested variabes
const queryNested = useQuery(
[
'key',
{
nested: {
props: [1, 2],
},
},
],
(key, variables) => Promise.resolve(variables.nested.props[0]),
);
queryNested.data; // $ExpectType number | undefined
}
function queryWithComplexKeysAndVariables() {
useQuery(['key', { a: 1 }], [{ b: { x: 1 } }, { c: { x: 1 } }], (
key1, // $ExpectType string
key2, // ExpectType { a: number }
var1, // $ExpectType { b: { x: number; }; }
var2, // $ExpectType { c: { x: number; }; }
) => Promise.resolve(key1 === 'key' && key2.a === 1 && var1.b.x === 1 && var2.c.x === 1));
// custom key
const longKey: [string, ...number[]] = ['key', 1, 2, 3, 4, 5];
useQuery(
longKey,
async (
key, // $ExpectType string
...ids // $ExpectType number[]
) => 100,
).data; // $ExpectType number | undefined
const longVariables: [boolean, ...object[]] = [true, {}];
useQuery(
['key'],
longVariables,
async (
key, // $ExpectType string
var1, // $ExpectType boolean
...vars // $ExpectType object[]
) => 100,
).data; // $ExpectType number | undefined
// the following example cannot work properly, as it would require concatenating tuples with infinite tails.
// ts-toolbelt library's `List.Concat` cannot do the job. It would be possible to do with `typescript-tuple` and additional trick.
// useQuery<number, typeof longKey, typeof longVariables>(longKey, longVariables, async (
// key, // $ExpectType string // <-- currently boolean?!
// keyOrVar, // $ExpectType number | boolean // <-- currently object
// ...rest // $ExpectType number | object // <-- currently object[]
// ) => 100).data; // $ExpectType number | undefined
}
function paginatedQuery() {
// Paginated mode
const queryPaginated = usePaginatedQuery('key', () => Promise.resolve({ data: [1, 2, 3], next: true }), {
refetchInterval: 1000,
});
queryPaginated.resolvedData; // $ExpectType { data: number[]; next: boolean; } | undefined
queryPaginated.latestData; // $ExpectType { data: number[]; next: boolean; } | undefined
queryPaginated.data; // $ExpectError
// Discriminated union over status
if (queryPaginated.status === 'loading') {
queryPaginated.resolvedData; // $ExpectType { data: number[]; next: boolean; } | undefined
queryPaginated.latestData; // $ExpectType { data: number[]; next: boolean; } | undefined
queryPaginated.error; // $ExpectType unknown
}
}], (variables) => Promise.resolve(variables.nested.props[0]));
queryNested.data; // $ExpectType number | null
// Paginated mode
const queryPaginated = useQuery('key', () => Promise.resolve({data: [1, 2, 3], next: true}), {
refetchInterval: 1000,
paginated: true,
getCanFetchMore: (lastPage, allPages) => lastPage.next
});
queryPaginated.data; // $ExpectType { data: number[]; next: boolean; }[] | null
queryPaginated.fetchMore; // $ExpectType (variables?: {} | undefined) => Promise<{ data: number[]; next: boolean; }> || (variables?: object | undefined) => Promise<{ data: number[]; next: boolean; }>
queryPaginated.canFetchMore; // $ExpectType boolean
queryPaginated.isFetchingMore; // $ExpectType boolean
if (queryPaginated.status === 'error') {
queryPaginated.resolvedData; // $ExpectType { data: number[]; next: boolean; } | undefined
queryPaginated.latestData; // $ExpectType { data: number[]; next: boolean; } | undefined
queryPaginated.error; // $ExpectType unknown
}
// Paginated mode - check if getCanFetchMore is required
useQuery('key', () => Promise.resolve(), {paginated: true}); // $ExpectError
if (queryPaginated.status === 'success') {
queryPaginated.resolvedData; // $ExpectType { data: number[]; next: boolean; }
queryPaginated.latestData; // $ExpectType { data: number[]; next: boolean; }
queryPaginated.error; // $ExpectType null
}
}
// Simple mutation
const mutation = () => Promise.resolve(['foo', 1]);
function simpleInfiniteQuery(condition: boolean) {
async function fetchWithCursor(key: string, cursor?: string) {
return [1, 2, 3];
}
function getFetchMore(last: number[], all: number[][]) {
return last.length ? String(all.length + 1) : false;
}
const [mutate] = useMutation(mutation, {
refetchQueries: ['todos', ['todo', { id: 5 }], 'reminders'],
refetchQueriesOnFailure: false
});
mutate();
mutate(undefined, {
updateQuery: 'query',
waitForRefetchQueries: false
});
useInfiniteQuery<number[], [string], string>(['key'], fetchWithCursor, {
getFetchMore: (
last, // $ExpectType number[]
all, // $ExpectType number[][]
) => 'next',
});
useInfiniteQuery(['key'], fetchWithCursor, { getFetchMore });
useInfiniteQuery('key', fetchWithCursor, { getFetchMore });
useInfiniteQuery(() => condition && 'key', fetchWithCursor, { getFetchMore });
// Invalid mutatation funciton
useMutation((arg1: string, arg2: string) => Promise.resolve()); // $ExpectError
useMutation((arg1: string) => null); // $ExpectError
const infiniteQuery = useInfiniteQuery(['key'], fetchWithCursor, { getFetchMore });
// Mutation with variables
const [mutateWithVars] = useMutation(
({param}: {param: number}) => Promise.resolve(Boolean(param)),
{
refetchQueries: ['todos', ['todo', { id: 5 }], 'reminders'],
refetchQueriesOnFailure: false
});
// The next example does not work; the type for cursor does not get inferred.
// useInfiniteQuery(['key'], fetchWithCursor, {
// getFetchMore: (last, all) => 'string',
// });
mutateWithVars({param: 1}, {
updateQuery: 'query',
waitForRefetchQueries: false
});
infiniteQuery.data; // $ExpectType number[][]
infiniteQuery.fetchMore(); // $ExpectType Promise<number[][]> | undefined
infiniteQuery.fetchMore('next'); // $ExpectType Promise<number[][]> | undefined
}
mutateWithVars({param: 'test'}); // $ExpectError
function log(...args: any[]) {}
function infiniteQueryWithVariables(condition: boolean) {
async function fetchWithCursor2(key: string, debuglog?: (...args: any[]) => void, cursor?: string) {
if (debuglog) debuglog(key, cursor);
return [1, 2, 3];
}
function getFetchMore(last: number[], all: number[][]) {
return last.length ? String(all.length + 1) : false;
}
useInfiniteQuery<number[], [string], [undefined | ((...args: any[]) => void)], string>(
['key'],
[undefined],
fetchWithCursor2,
{
getFetchMore: (last, all) => 'next',
},
);
useInfiniteQuery(['key'], [log], fetchWithCursor2, { getFetchMore });
useInfiniteQuery('key', [log], fetchWithCursor2, { getFetchMore });
useInfiniteQuery(() => condition && 'key', [log], fetchWithCursor2, { getFetchMore });
}
function simpleMutation() {
// Simple mutation
const mutation = () => Promise.resolve(['foo', 'bar']);
const [mutate] = useMutation(mutation, {
onSuccess(result) {
result; // $ExpectType string[]
},
});
mutate();
mutate(undefined, {
throwOnError: true,
onSettled(result, error) {
result; // $ExpectType string[] | undefined
error; // $ExpectType unknown
},
});
// Invalid mutatation funciton
useMutation((arg1: string, arg2: string) => Promise.resolve()); // $ExpectError
useMutation((arg1: string) => null); // $ExpectError
}
function mutationWithVariables() {
// Mutation with variables
const [mutateWithVars] = useMutation(({ param }: { param: number }) => Promise.resolve(Boolean(param)), {
useErrorBoundary: true,
onMutate(variables) {
variables; // $ExpectType { param: number; }
return { snapshot: variables.param };
},
});
mutateWithVars(
{ param: 1 },
{
async onSuccess(data) {
data; // $ExpectType boolean
},
},
);
mutateWithVars({ param: 'test' }); // $ExpectError
}
function helpers() {
useIsFetching(); // $ExpectType number
setConsole({ log, error: log, warn: log });
}
function globalConfig() {
const globalConfig: ReactQueryProviderConfig = {
onError(err, snapshot) {
log('Error', err, snapshot);
},
onMutate(variables) {
log(variables);
},
suspense: true,
};
}
function dataDiscriminatedUnion() {
// Query Variables
const param = 'test';
const queryResult = useQuery(['todos', { param }], (key, variables) => Promise.resolve([param]));
queryResult.data; // $ExpectType string[] | undefined
// Discriminated union over status
if (queryResult.status === 'loading') {
queryResult.data; // $ExpectType string[] | undefined
queryResult.error; // $ExpectType unknown
}
if (queryResult.status === 'error') {
// disabled
queryResult.data; // $ExpectType string[] | undefined
queryResult.error; // $ExpectType unknown
}
if (queryResult.status === 'success') {
// disabled
queryResult.data; // $ExpectType string[]
queryResult.error; // $ExpectType null
}
}
function mutationStatusDiscriminatedUnion() {
const mutation = () => Promise.resolve(['foo', 'bar']);
const [mutate, mutationState] = useMutation(mutation);
mutate();
// enabled
// TODO: handle invalid argument passed to mutationFn
// mutate('arg'); // $ExpectError
mutate('arg'); // $ExpectError
mutationState.data; // $ExpectType string[] | undefined
// Discriminated union over status
if (mutationState.status === 'idle') {
mutationState.data; // $ExpectType undefined
mutationState.error; // $ExpectType null
}
if (mutationState.status === 'loading') {
mutationState.data; // $ExpectType undefined
// corrected
// mutationState.error; // $ExpectType null
mutationState.error; // $ExpectType undefined
}
if (mutationState.status === 'error') {
mutationState.data; // $ExpectType undefined
mutationState.error; // $ExpectType unknown
}
if (mutationState.status === 'success') {
mutationState.data; // $ExpectType string[]
mutationState.error; // $ExpectType undefined
}
}