import { assertType, describe, expectTypeOf, it } from 'vitest' import { QueryClient } from '@tanstack/query-core' import { queryKey } from '@tanstack/query-test-utils' import { createMutation, mutationOptions, useIsMutating, useMutationState, } from '../../src/index.js' import type { DefaultError, MutationFunctionContext, MutationState, WithRequired, } from '@tanstack/query-core' import type { CreateMutationOptions, CreateMutationResult, } from '../../src/types.js' describe('mutationOptions', () => { it('should not allow excess properties', () => { const key = queryKey() // @ts-expect-error this is a good error, because onMutates does not exist! mutationOptions({ mutationFn: () => Promise.resolve(5), mutationKey: key, onMutates: 1000, onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }) }) it('should infer types for callbacks', () => { const key = queryKey() mutationOptions({ mutationFn: () => Promise.resolve(5), mutationKey: key, onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }) }) it('should infer types for onError callback', () => { const key = queryKey() mutationOptions({ mutationFn: () => { throw new Error('fail') }, mutationKey: key, onError: (error) => { expectTypeOf(error).toEqualTypeOf() }, }) }) it('should infer types for variables', () => { const key = queryKey() mutationOptions({ mutationFn: (vars) => { expectTypeOf(vars).toEqualTypeOf<{ id: string }>() return Promise.resolve(5) }, mutationKey: key, }) }) it('should infer result type correctly', () => { const key = queryKey() mutationOptions({ mutationFn: () => Promise.resolve(5), mutationKey: key, onMutate: () => { return { name: 'onMutateResult' } }, onSuccess: (_data, _variables, onMutateResult) => { expectTypeOf(onMutateResult).toEqualTypeOf<{ name: string }>() }, }) }) it('should infer context type correctly', () => { const key = queryKey() mutationOptions({ mutationFn: (_variables, context) => { expectTypeOf(context).toEqualTypeOf() return Promise.resolve(5) }, mutationKey: key, onMutate: (_variables, context) => { expectTypeOf(context).toEqualTypeOf() }, onSuccess: (_data, _variables, _onMutateResult, context) => { expectTypeOf(context).toEqualTypeOf() }, onError: (_error, _variables, _onMutateResult, context) => { expectTypeOf(context).toEqualTypeOf() }, onSettled: (_data, _error, _variables, _onMutateResult, context) => { expectTypeOf(context).toEqualTypeOf() }, }) }) it('should error if mutationFn return type mismatches TData', () => { assertType( mutationOptions({ // @ts-expect-error this is a good error, because return type is string, not number mutationFn: async () => Promise.resolve('wrong return'), }), ) }) it('should allow mutationKey to be omitted', () => { return mutationOptions({ mutationFn: () => Promise.resolve(123), onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }) }) it('should infer all types when not explicitly provided', () => { const key = queryKey() expectTypeOf( mutationOptions({ mutationFn: (id: string) => Promise.resolve(id.length), mutationKey: key, onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }), ).toEqualTypeOf< WithRequired< CreateMutationOptions, 'mutationKey' > >() expectTypeOf( mutationOptions({ mutationFn: (id: string) => Promise.resolve(id.length), onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }), ).toEqualTypeOf< Omit, 'mutationKey'> >() }) it('should work when used with createMutation', () => { const key = queryKey() const mutation = createMutation(() => mutationOptions({ mutationKey: key, mutationFn: () => Promise.resolve('data'), onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }), ) expectTypeOf(mutation).toEqualTypeOf< CreateMutationResult >() createMutation(() => // should allow when used with createMutation without mutationKey mutationOptions({ mutationFn: () => Promise.resolve('data'), onSuccess: (data) => { expectTypeOf(data).toEqualTypeOf() }, }), ) }) it('should work when used with useIsMutating', () => { const key = queryKey() const isMutating = useIsMutating( mutationOptions({ mutationKey: key, mutationFn: () => Promise.resolve(5), }), ) expectTypeOf(isMutating.current).toEqualTypeOf() useIsMutating( // @ts-expect-error filters should have mutationKey mutationOptions({ mutationFn: () => Promise.resolve(5), }), ) }) it('should work when used with queryClient.isMutating', () => { const key = queryKey() const queryClient = new QueryClient() const isMutating = queryClient.isMutating( mutationOptions({ mutationKey: key, mutationFn: () => Promise.resolve(5), }), ) expectTypeOf(isMutating).toEqualTypeOf() queryClient.isMutating( // @ts-expect-error filters should have mutationKey mutationOptions({ mutationFn: () => Promise.resolve(5), }), ) }) it('should work when used with useMutationState', () => { const key = queryKey() const mutationState = useMutationState({ filters: mutationOptions({ mutationKey: key, mutationFn: () => Promise.resolve(5), }), }) expectTypeOf(mutationState).toEqualTypeOf< Array> >() useMutationState({ // @ts-expect-error filters should have mutationKey filters: mutationOptions({ mutationFn: () => Promise.resolve(5), }), }) }) })