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
10 changes: 10 additions & 0 deletions .changeset/dog-brown-fox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@tanstack/react-query': patch
'@tanstack/preact-query': patch
'@tanstack/solid-query': patch
'@tanstack/vue-query': patch
'@tanstack/svelte-query': patch
'@tanstack/lit-query': patch
---

fix(types): add `TMutation` generic to `useMutationState` with caller-side assertion warning in JSDoc
40 changes: 33 additions & 7 deletions packages/lit-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,29 @@ import {
} from './accessor.js'
import { BaseController } from './controllers/BaseController.js'

type MutationTypeFromResult<TResult> = [TResult] extends [
MutationState<
infer TData,
infer TError,
infer TVariables,
infer TOnMutateResult
>,
]
? Mutation<TData, TError, TVariables, TOnMutateResult>
: Mutation

/**
* Options accepted by `useMutationState`.
*/
export type MutationStateOptions<TResult> = {
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> = MutationTypeFromResult<TResult>,
> 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 @@ -188,15 +206,23 @@ class MutationStateController<TResult> extends BaseController<TResult[]> {
* }
* }
* ```
*
* @template TResult - The type of values returned by the `select` callback.
* @template TMutation - Narrows the type of the `mutation` argument passed to
* `select`. This is a caller-side assertion β€” the mutation cache stores
* mutations as the base `Mutation` type, so it is the caller's responsibility
* to ensure `TMutation` matches the actual mutations in the cache (e.g. by
* specifying a `mutationKey` in `filters`).
*/
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
47 changes: 40 additions & 7 deletions packages/preact-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,58 @@ export function useIsMutating(
).length
}

type MutationStateOptions<TResult = MutationState> = {
type MutationTypeFromResult<TResult> = [TResult] extends [
MutationState<
infer TData,
infer TError,
infer TVariables,
infer TOnMutateResult
>,
]
? Mutation<TData, TError, TVariables, TOnMutateResult>
: Mutation

type MutationStateOptions<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
> = {
filters?: MutationFilters
select?: (mutation: Mutation) => TResult
select?: (mutation: TMutation) => TResult
}

function getResult<TResult = MutationState>(
function getResult<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
mutationCache: MutationCache,
options: MutationStateOptions<TResult>,
options: MutationStateOptions<TResult, TMutation>,
): Array<TResult> {
return mutationCache
.findAll(options.filters)
.map(
(mutation): TResult =>
(options.select ? options.select(mutation) : mutation.state) as TResult,
(options.select
? options.select(mutation as TMutation)
: mutation.state) as TResult,
)
}

export function useMutationState<TResult = MutationState>(
options: MutationStateOptions<TResult> = {},
/**
* @template TResult - The type of values returned by the `select` callback.
* @template TMutation - Narrows the type of the `mutation` argument passed to
* `select`. This is a caller-side assertion β€” the mutation cache stores
* mutations as the base `Mutation` type, so it is the caller's responsibility
* to ensure `TMutation` matches the actual mutations in the cache (e.g. by
* specifying a `mutationKey` in `filters`).
*/
export function useMutationState<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
options: MutationStateOptions<TResult, TMutation> = {},
queryClient?: QueryClient,
): Array<TResult> {
const mutationCache = useQueryClient(queryClient).getMutationCache()
Expand Down
47 changes: 40 additions & 7 deletions packages/react-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,58 @@ export function useIsMutating(
).length
}

type MutationStateOptions<TResult = MutationState> = {
type MutationTypeFromResult<TResult> = [TResult] extends [
MutationState<
infer TData,
infer TError,
infer TVariables,
infer TOnMutateResult
>,
]
? Mutation<TData, TError, TVariables, TOnMutateResult>
: Mutation

type MutationStateOptions<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
> = {
filters?: MutationFilters
select?: (mutation: Mutation) => TResult
select?: (mutation: TMutation) => TResult
}

function getResult<TResult = MutationState>(
function getResult<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
mutationCache: MutationCache,
options: MutationStateOptions<TResult>,
options: MutationStateOptions<TResult, TMutation>,
): Array<TResult> {
return mutationCache
.findAll(options.filters)
.map(
(mutation): TResult =>
(options.select ? options.select(mutation) : mutation.state) as TResult,
(options.select
? options.select(mutation as TMutation)
: mutation.state) as TResult,
)
}

export function useMutationState<TResult = MutationState>(
options: MutationStateOptions<TResult> = {},
/**
* @template TResult - The type of values returned by the `select` callback.
* @template TMutation - Narrows the type of the `mutation` argument passed to
* `select`. This is a caller-side assertion β€” the mutation cache stores
* mutations as the base `Mutation` type, so it is the caller's responsibility
* to ensure `TMutation` matches the actual mutations in the cache (e.g. by
* specifying a `mutationKey` in `filters`).
*/
export function useMutationState<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
options: MutationStateOptions<TResult, TMutation> = {},
queryClient?: QueryClient,
): Array<TResult> {
const mutationCache = useQueryClient(queryClient).getMutationCache()
Expand Down
47 changes: 40 additions & 7 deletions packages/solid-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,58 @@ import type {
import type { Accessor } from 'solid-js'
import type { QueryClient } from './QueryClient'

type MutationStateOptions<TResult = MutationState> = {
type MutationTypeFromResult<TResult> = [TResult] extends [
MutationState<
infer TData,
infer TError,
infer TVariables,
infer TOnMutateResult
>,
]
? Mutation<TData, TError, TVariables, TOnMutateResult>
: Mutation

type MutationStateOptions<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
> = {
filters?: MutationFilters
select?: (mutation: Mutation) => TResult
select?: (mutation: TMutation) => TResult
}

function getResult<TResult = MutationState>(
function getResult<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
mutationCache: MutationCache,
options: MutationStateOptions<TResult>,
options: MutationStateOptions<TResult, TMutation>,
): Array<TResult> {
return mutationCache
.findAll(options.filters)
.map(
(mutation): TResult =>
(options.select ? options.select(mutation) : mutation.state) as TResult,
(options.select
? options.select(mutation as TMutation)
: mutation.state) as TResult,
)
}

export function useMutationState<TResult = MutationState>(
options: Accessor<MutationStateOptions<TResult>> = () => ({}),
/**
* @template TResult - The type of values returned by the `select` callback.
* @template TMutation - Narrows the type of the `mutation` argument passed to
* `select`. This is a caller-side assertion β€” the mutation cache stores
* mutations as the base `Mutation` type, so it is the caller's responsibility
* to ensure `TMutation` matches the actual mutations in the cache (e.g. by
* specifying a `mutationKey` in `filters`).
*/
export function useMutationState<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
options: Accessor<MutationStateOptions<TResult, TMutation>> = () => ({}),
queryClient?: Accessor<QueryClient>,
): Accessor<Array<TResult>> {
const client = createMemo(() => useQueryClient(queryClient?.()))
Expand Down
21 changes: 17 additions & 4 deletions packages/svelte-query/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,25 @@ export type CreateMutationResult<
TOnMutateResult = unknown,
> = CreateBaseMutationResult<TData, TError, TVariables, TOnMutateResult>

export type MutationTypeFromResult<TResult> = [TResult] extends [
MutationState<
infer TData,
infer TError,
infer TVariables,
infer TOnMutateResult
>,
]
? Mutation<TData, TError, TVariables, TOnMutateResult>
: Mutation

/** Options for useMutationState */
export type MutationStateOptions<TResult = MutationState> = {
export type MutationStateOptions<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
> = {
filters?: MutationFilters
select?: (
mutation: Mutation<unknown, DefaultError, unknown, unknown>,
) => TResult
select?: (mutation: TMutation) => TResult
}

export type QueryClientProviderProps = {
Expand Down
31 changes: 25 additions & 6 deletions packages/svelte-query/src/useMutationState.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
import { replaceEqualDeep } from '@tanstack/query-core'
import { useQueryClient } from './useQueryClient.js'
import type {
Mutation,
MutationCache,
MutationState,
QueryClient,
} from '@tanstack/query-core'
import type { MutationStateOptions } from './types.js'
import type { MutationStateOptions, MutationTypeFromResult } from './types.js'

function getResult<TResult = MutationState>(
function getResult<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
mutationCache: MutationCache,
options: MutationStateOptions<TResult>,
options: MutationStateOptions<TResult, TMutation>,
): Array<TResult> {
return mutationCache
.findAll(options.filters)
.map(
(mutation): TResult =>
(options.select ? options.select(mutation) : mutation.state) as TResult,
(options.select
? options.select(mutation as TMutation)
: mutation.state) as TResult,
)
}

export function useMutationState<TResult = MutationState>(
options: MutationStateOptions<TResult> = {},
/**
* @template TResult - The type of values returned by the `select` callback.
* @template TMutation - Narrows the type of the `mutation` argument passed to
* `select`. This is a caller-side assertion β€” the mutation cache stores
* mutations as the base `Mutation` type, so it is the caller's responsibility
* to ensure `TMutation` matches the actual mutations in the cache (e.g. by
* specifying a `mutationKey` in `filters`).
*/
export function useMutationState<
TResult = MutationState,
TMutation extends Mutation<any, any, any, any> =
MutationTypeFromResult<TResult>,
>(
options: MutationStateOptions<TResult, TMutation> = {},
queryClient?: QueryClient,
): Array<TResult> {
const mutationCache = useQueryClient(queryClient).getMutationCache()
Expand Down
Loading