diff --git a/.changeset/lit-mutation-state-generics.md b/.changeset/lit-mutation-state-generics.md new file mode 100644 index 00000000000..95d828c3be7 --- /dev/null +++ b/.changeset/lit-mutation-state-generics.md @@ -0,0 +1,5 @@ +--- +'@tanstack/lit-query': patch +--- + +fix(types): propagate generic type parameters to the `useMutationState` select callback diff --git a/packages/lit-query/src/tests/mutation-state-types.test.ts b/packages/lit-query/src/tests/mutation-state-types.test.ts new file mode 100644 index 00000000000..b5648923569 --- /dev/null +++ b/packages/lit-query/src/tests/mutation-state-types.test.ts @@ -0,0 +1,27 @@ +import { QueryClient } from '@tanstack/query-core' +import { describe, expectTypeOf, it } from 'vitest' +import { useMutationState } from '../useMutationState.js' +import { TestControllerHost } from './testHost.js' +import type { Mutation, MutationState } from '@tanstack/query-core' + +describe('useMutationState types', () => { + it('propagates typed mutation state generics to the select callback', () => { + type MyData = { data: Array } + type MyError = { code: number; message: string } + type MyVariables = { id: number } + + useMutationState>( + new TestControllerHost(), + { + filters: { mutationKey: ['key'] }, + select: (mutation) => { + expectTypeOf(mutation).toEqualTypeOf< + Mutation + >() + return mutation.state + }, + }, + new QueryClient(), + ) + }) +}) diff --git a/packages/lit-query/src/useMutationState.ts b/packages/lit-query/src/useMutationState.ts index ed362ea0944..18d29e10e07 100644 --- a/packages/lit-query/src/useMutationState.ts +++ b/packages/lit-query/src/useMutationState.ts @@ -17,11 +17,26 @@ import { BaseController } from './controllers/BaseController.js' /** * Options accepted by `useMutationState`. */ -export type MutationStateOptions = { +type MutationTypeFromResult = [TResult] extends [ + MutationState< + infer TData, + infer TError, + infer TVariables, + infer TOnMutateResult + >, +] + ? Mutation + : Mutation + +export type MutationStateOptions< + TResult, + TMutation extends Mutation = + MutationTypeFromResult, +> = { /** Filters used to select mutations from the mutation cache. */ filters?: Accessor /** Maps each matching mutation to the value returned by the accessor. */ - select?: (mutation: Mutation) => TResult + select?: (mutation: TMutation) => TResult } /** @@ -35,13 +50,16 @@ export type MutationStateAccessor = ValueAccessor & { destroy: () => void } -class MutationStateController extends BaseController { +class MutationStateController< + TResult, + TMutation extends Mutation, +> extends BaseController { private queryClient: QueryClient | undefined private unsubscribe: (() => void) | undefined constructor( host: ReactiveControllerHost, - private readonly options: MutationStateOptions, + private readonly options: MutationStateOptions, queryClient?: QueryClient, ) { super(host, [], queryClient) @@ -148,7 +166,7 @@ class MutationStateController extends BaseController { return mutations.map((mutation) => { if (select) { - return select(mutation) + return select(mutation as TMutation) } return mutation.state as TResult @@ -191,12 +209,18 @@ class MutationStateController extends BaseController { */ export function useMutationState< TResult = MutationState, + TMutation extends Mutation = + MutationTypeFromResult, >( host: ReactiveControllerHost, - options: MutationStateOptions = {}, + options: MutationStateOptions = {}, queryClient?: QueryClient, ): MutationStateAccessor { - const controller = new MutationStateController(host, options, queryClient) + const controller = new MutationStateController( + host, + options, + queryClient, + ) return Object.assign( createValueAccessor(() => controller.current), {