diff --git a/packages/query-core/src/__tests__/mutations.test-d.tsx b/packages/query-core/src/__tests__/mutations.test-d.tsx new file mode 100644 index 0000000000..f5f15afdfc --- /dev/null +++ b/packages/query-core/src/__tests__/mutations.test-d.tsx @@ -0,0 +1,135 @@ +import { describe, expectTypeOf, it } from 'vitest' +import type { DefaultError, MutateFunction, MutateOptions } from 'src/types' + +describe('MutateFunction', () => { + it('void variables', () => { + const mutate = {} as MutateFunction + + expectTypeOf<Parameters<typeof mutate>[0]>().toEqualTypeOf< + undefined | void + >() + + expectTypeOf<Parameters<typeof mutate>[1]>().toEqualTypeOf< + undefined | MutateOptions<unknown, DefaultError, void, unknown> + >() + + mutate() // can be called with no arguments + mutate(undefined, { + onError: (e) => { + expectTypeOf(e).toEqualTypeOf<DefaultError>() + }, + }) + }) + + it('optional undefinable variables', () => { + const mutate = {} as MutateFunction< + unknown, + DefaultError, + number | undefined, + unknown + > + + expectTypeOf<Parameters<typeof mutate>[0]>().toEqualTypeOf< + number | undefined + >() + + expectTypeOf<Parameters<typeof mutate>[1]>().toEqualTypeOf< + | undefined + | MutateOptions<unknown, DefaultError, number | undefined, unknown> + >() + + mutate() // can be called with no arguments + mutate(undefined, { + onError: (e) => { + expectTypeOf(e).toEqualTypeOf<DefaultError>() + }, + }) + }) + + it('required non-undefinable variables', () => { + const mutate = {} as MutateFunction<unknown, DefaultError, number, unknown> + + expectTypeOf<Parameters<typeof mutate>[0]>().toEqualTypeOf<number>() + + expectTypeOf<Parameters<typeof mutate>[1]>().toEqualTypeOf< + undefined | MutateOptions<unknown, DefaultError, number, unknown> + >() + + // @ts-expect-error --- required variables + mutate() + mutate(123, { + onError: (e) => { + expectTypeOf(e).toEqualTypeOf<DefaultError>() + }, + }) + }) + + describe('compatible with spread arguments pattern', () => { + // this is common pattern used internal so we need make sure it still works + + it('void variables', () => { + const mutate = {} as (...options: Parameters<MutateFunction>) => void + + expectTypeOf<Parameters<typeof mutate>[0]>().toEqualTypeOf< + undefined | void + >() + + expectTypeOf<Parameters<typeof mutate>[1]>().toEqualTypeOf< + undefined | MutateOptions<unknown, DefaultError, void, unknown> + >() + + mutate() // can be called with no arguments + mutate(undefined, { + onError: (e) => { + expectTypeOf(e).toEqualTypeOf<DefaultError>() + }, + }) + }) + + it('optional undefinable variables', () => { + const mutate = {} as ( + ...options: Parameters< + MutateFunction<unknown, DefaultError, number | undefined, unknown> + > + ) => void + + expectTypeOf<Parameters<typeof mutate>[0]>().toEqualTypeOf< + number | undefined + >() + + expectTypeOf<Parameters<typeof mutate>[1]>().toEqualTypeOf< + | undefined + | MutateOptions<unknown, DefaultError, number | undefined, unknown> + >() + + mutate() // can be called with no arguments + mutate(undefined, { + onError: (e) => { + expectTypeOf(e).toEqualTypeOf<DefaultError>() + }, + }) + }) + + it('required non-undefinable variables', () => { + const mutate = {} as ( + ...options: Parameters< + MutateFunction<unknown, DefaultError, number, unknown> + > + ) => void + + expectTypeOf<Parameters<typeof mutate>[0]>().toEqualTypeOf<number>() + + expectTypeOf<Parameters<typeof mutate>[1]>().toEqualTypeOf< + undefined | MutateOptions<unknown, DefaultError, number, unknown> + >() + + // @ts-expect-error --- required variables + mutate() + mutate(123, { + onError: (e) => { + expectTypeOf(e).toEqualTypeOf<DefaultError>() + }, + }) + }) + }) +}) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index c973b6a758..c15419cad4 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -1161,14 +1161,28 @@ export interface MutateOptions< ) => void } +export type MutateFunctionRest< + TData = unknown, + TError = DefaultError, + TVariables = void, + TContext = unknown, +> = undefined extends TVariables + ? [ + variables?: TVariables, + options?: MutateOptions<TData, TError, TVariables, TContext>, + ] + : [ + variables: TVariables, + options?: MutateOptions<TData, TError, TVariables, TContext>, + ] + export type MutateFunction< TData = unknown, TError = DefaultError, TVariables = void, TContext = unknown, > = ( - variables: TVariables, - options?: MutateOptions<TData, TError, TVariables, TContext>, + ...rest: MutateFunctionRest<TData, TError, TVariables, TContext> ) => Promise<TData> export interface MutationObserverBaseResult<