From a8a42908bf70c2472a3bf37f076a87a318a44ffe Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Thu, 1 Oct 2026 01:12:32 +0900 Subject: [PATCH] test(lit-query/createMutationController): add type tests --- .../createMutationController.test-d.ts | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 packages/lit-query/src/__tests__/createMutationController.test-d.ts diff --git a/packages/lit-query/src/__tests__/createMutationController.test-d.ts b/packages/lit-query/src/__tests__/createMutationController.test-d.ts new file mode 100644 index 0000000000..ecce6f8d69 --- /dev/null +++ b/packages/lit-query/src/__tests__/createMutationController.test-d.ts @@ -0,0 +1,249 @@ +import { QueryClient } from '@tanstack/query-core' +import { LitElement } from 'lit' +import { describe, expectTypeOf, it } from 'vitest' +import { createMutationController } from '../createMutationController.js' +import type { + DefaultError, + MutationFunctionContext, + MutationKey, +} from '@tanstack/query-core' +import type { MutationResultAccessor } from '../createMutationController.js' + +class Host extends LitElement {} + +describe('createMutationController', () => { + it('should infer TData from mutationFn return type', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve('data'), + }, + new QueryClient(), + ) + + expectTypeOf(mutation().data).toEqualTypeOf() + expectTypeOf(mutation().error).toEqualTypeOf() + }) + + it('should infer TVariables from mutationFn parameter', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: (vars: { id: string }) => Promise.resolve(vars.id), + }, + new QueryClient(), + ) + + expectTypeOf(mutation.mutate).toBeCallableWith({ id: '1' }) + expectTypeOf(mutation().data).toEqualTypeOf() + }) + + it('should infer TOnMutateResult from onMutate return type', () => { + createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve('data'), + onMutate: () => { + return { token: 'abc' } + }, + onSuccess: (_data, _variables, onMutateResult) => { + expectTypeOf(onMutateResult).toEqualTypeOf<{ token: string }>() + }, + onError: (_error, _variables, onMutateResult) => { + expectTypeOf(onMutateResult).toEqualTypeOf< + { token: string } | undefined + >() + }, + }, + new QueryClient(), + ) + }) + + it('should allow explicit generic types', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: (vars) => { + expectTypeOf(vars).toEqualTypeOf<{ id: number }>() + return Promise.resolve('result') + }, + }, + new QueryClient(), + ) + + expectTypeOf(mutation().data).toEqualTypeOf() + expectTypeOf(mutation().error).toEqualTypeOf() + }) + + it('should return correct MutationResultAccessor type', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve(42), + }, + new QueryClient(), + ) + + expectTypeOf(mutation).toEqualTypeOf< + MutationResultAccessor + >() + }) + + it('should type mutateAsync with correct return type', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: (id: string) => Promise.resolve(id.length), + }, + new QueryClient(), + ) + + expectTypeOf(mutation.mutateAsync).toBeCallableWith('test') + expectTypeOf(mutation.mutateAsync('test')).toEqualTypeOf>() + }) + + it('should default TVariables to void when mutationFn has no parameters', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve('data'), + }, + new QueryClient(), + ) + + expectTypeOf(mutation.mutate).toBeCallableWith() + }) + + it('should allow calling mutate with no arguments when TVariables is optional undefinable', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: (_variables: number | undefined) => Promise.resolve(1), + }, + new QueryClient(), + ) + + expectTypeOf(mutation.mutate).toBeCallableWith() + expectTypeOf(mutation.mutateAsync).toBeCallableWith() + }) + + it('should infer custom TError type', () => { + class CustomError extends Error { + code: number + constructor(code: number) { + super() + this.code = code + } + } + + const mutation = createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve('data'), + }, + new QueryClient(), + ) + + expectTypeOf(mutation().error).toEqualTypeOf() + expectTypeOf(mutation().data).toEqualTypeOf() + }) + + it('should infer types for onSettled callback', () => { + createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve(42), + onSettled: (data, error, _variables, _onMutateResult) => { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + }, + }, + new QueryClient(), + ) + }) + + it('should infer custom TError in onError callback', () => { + class CustomError extends Error { + code: number + constructor(code: number) { + super() + this.code = code + } + } + + createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve('data'), + onError: (error) => { + expectTypeOf(error).toEqualTypeOf() + }, + }, + new QueryClient(), + ) + }) + + it('should type context as the last argument for mutationFn and every hook-level callback', () => { + createMutationController( + new Host(), + { + mutationFn: (_variables, context) => { + expectTypeOf(context).toEqualTypeOf() + expectTypeOf(context.client).toEqualTypeOf() + return Promise.resolve('data') + }, + 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() + }, + }, + new QueryClient(), + ) + }) + + it('should type context as the last argument for every per-call mutate option', () => { + const mutation = createMutationController( + new Host(), + { + mutationFn: () => Promise.resolve('data'), + }, + new QueryClient(), + ) + + mutation.mutate(undefined, { + 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 type context.mutationKey as MutationKey', () => { + createMutationController( + new Host(), + { + mutationKey: ['todos', 'add'] as const, + mutationFn: () => Promise.resolve('data'), + onSuccess: (_data, _variables, _onMutateResult, context) => { + expectTypeOf(context.mutationKey).toEqualTypeOf< + MutationKey | undefined + >() + }, + }, + new QueryClient(), + ) + }) +})