Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lit-mutation-state-generics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/lit-query': patch
---

fix(types): propagate generic type parameters to the `useMutationState` select callback
27 changes: 27 additions & 0 deletions packages/lit-query/src/tests/mutation-state-types.test.ts
Original file line number Diff line number Diff line change
@@ -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<string> }
type MyError = { code: number; message: string }
type MyVariables = { id: number }

useMutationState<MutationState<MyData, MyError, MyVariables>>(
new TestControllerHost(),
{
filters: { mutationKey: ['key'] },
select: (mutation) => {
expectTypeOf(mutation).toEqualTypeOf<
Mutation<MyData, MyError, MyVariables, unknown>
>()
return mutation.state
},
},
new QueryClient(),
)
})
})
38 changes: 31 additions & 7 deletions packages/lit-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,26 @@ import { BaseController } from './controllers/BaseController.js'
/**
* Options accepted by `useMutationState`.
*/
export type MutationStateOptions<TResult> = {
type MutationTypeFromResult<TResult> = [TResult] extends [
MutationState<
infer TData,
infer TError,
infer TVariables,
infer TOnMutateResult
>,
]
? Mutation<TData, TError, TVariables, TOnMutateResult>
: Mutation

export type MutationStateOptions<
TResult,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
> = {
/** Filters used to select mutations from the mutation cache. */
filters?: Accessor<MutationFilters>
/** Maps each matching mutation to the value returned by the accessor. */
select?: (mutation: Mutation) => TResult
select?: (mutation: TMutation) => TResult
}

/**
Expand All @@ -35,13 +50,16 @@ export type MutationStateAccessor<TResult> = ValueAccessor<TResult[]> & {
destroy: () => void
}

class MutationStateController<TResult> extends BaseController<TResult[]> {
class MutationStateController<
TResult,
TMutation extends Mutation<any, any, any, any>,
> extends BaseController<TResult[]> {
private queryClient: QueryClient | undefined
private unsubscribe: (() => void) | undefined

constructor(
host: ReactiveControllerHost,
private readonly options: MutationStateOptions<TResult>,
private readonly options: MutationStateOptions<TResult, TMutation>,
queryClient?: QueryClient,
) {
super(host, [], queryClient)
Expand Down Expand Up @@ -148,7 +166,7 @@ class MutationStateController<TResult> extends BaseController<TResult[]> {

return mutations.map((mutation) => {
if (select) {
return select(mutation)
return select(mutation as TMutation)
}

return mutation.state as TResult
Expand Down Expand Up @@ -191,12 +209,18 @@ class MutationStateController<TResult> extends BaseController<TResult[]> {
*/
export function useMutationState<
TResult = MutationState<unknown, unknown, unknown, unknown>,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
host: ReactiveControllerHost,
options: MutationStateOptions<TResult> = {},
options: MutationStateOptions<TResult, TMutation> = {},
queryClient?: QueryClient,
): MutationStateAccessor<TResult> {
const controller = new MutationStateController(host, options, queryClient)
const controller = new MutationStateController<TResult, TMutation>(
host,
options,
queryClient,
)
return Object.assign(
createValueAccessor(() => controller.current),
{
Expand Down