-
Notifications
You must be signed in to change notification settings - Fork 0
feat(react-query): add mutationOptions #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| id: mutationOptions | ||
| title: mutationOptions | ||
| --- | ||
|
|
||
| ```tsx | ||
| mutationOptions({ | ||
| mutationFn, | ||
| ...options, | ||
| }) | ||
| ``` | ||
|
|
||
| **Options** | ||
|
|
||
| You can generally pass everything to `mutationOptions` that you can also pass to [`useMutation`](./useMutation.md). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { describe, expectTypeOf, it } from 'vitest' | ||
| import { dataTagSymbol } from '@tanstack/query-core' | ||
| import { mutationOptions } from '../mutationOptions' | ||
|
|
||
| describe('mutationOptions', () => { | ||
| it('should not allow excess properties', () => { | ||
| return mutationOptions({ | ||
| mutationFn: () => Promise.resolve(5), | ||
| mutationKey: ['key'], | ||
| // @ts-expect-error this is a good error, because onMutates does not exist! | ||
| onMutates: 1000, | ||
| }) | ||
| }) | ||
|
|
||
| it('should infer types for callbacks', () => { | ||
| return mutationOptions({ | ||
| mutationFn: () => Promise.resolve(5), | ||
| mutationKey: ['key'], | ||
| onSuccess: (data) => { | ||
| expectTypeOf(data).toEqualTypeOf<number>() | ||
| }, | ||
| }) | ||
| }) | ||
|
|
||
| it('should tag the mutationKey with the result type of the MutationFn', () => { | ||
| const { mutationKey } = mutationOptions({ | ||
| mutationKey: ['key'], | ||
| mutationFn: () => Promise.resolve(5), | ||
| }) | ||
|
|
||
| expectTypeOf(mutationKey[dataTagSymbol]).toEqualTypeOf<number>() | ||
| }) | ||
|
|
||
| it('should tag the mutationKey with unknown if there is no mutationFn', () => { | ||
| const { mutationKey } = mutationOptions({ | ||
| mutationKey: ['key'], | ||
| }) | ||
|
|
||
| expectTypeOf(mutationKey[dataTagSymbol]).toEqualTypeOf<unknown>() | ||
| }) | ||
|
|
||
| it('should tag the mutationKey with the result type of the MutationFn if onSuccess is used', () => { | ||
| const { mutationKey } = mutationOptions({ | ||
| mutationKey: ['key'], | ||
| mutationFn: () => Promise.resolve(5), | ||
| onSuccess: () => {}, | ||
| }) | ||
|
|
||
| expectTypeOf(mutationKey[dataTagSymbol]).toEqualTypeOf<number>() | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import { mutationOptions } from '../mutationOptions' | ||
| import type { UseMutationOptions } from '../types' | ||
|
|
||
| describe('mutationOptions', () => { | ||
| it('should return the object received as a parameter without any modification.', () => { | ||
| const object: UseMutationOptions = { | ||
| mutationKey: ['key'], | ||
| mutationFn: () => Promise.resolve(5), | ||
| } as const | ||
|
|
||
| expect(mutationOptions(object)).toStrictEqual(object) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||
| import type { | ||||||||||
| DataTag, | ||||||||||
| DefaultError, | ||||||||||
| InitialDataFunction, | ||||||||||
| MutationFunction, | ||||||||||
| OmitKeyof, | ||||||||||
| SkipToken, | ||||||||||
| } from '@tanstack/query-core' | ||||||||||
| import type { UseMutationOptions } from './types' | ||||||||||
|
|
||||||||||
| export type UndefinedInitialDataOptions< | ||||||||||
| TMutationFnData = unknown, | ||||||||||
| TError = DefaultError, | ||||||||||
| TData = void, | ||||||||||
| TMutationKey = unknown, | ||||||||||
| > = UseMutationOptions<TMutationFnData, TError, TData, TMutationKey> & { | ||||||||||
| initialData?: | ||||||||||
| | undefined | ||||||||||
| | InitialDataFunction<NonUndefinedGuard<TMutationFnData>> | ||||||||||
| | NonUndefinedGuard<TMutationFnData> | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export type UnusedSkipTokenOptions< | ||||||||||
| TMutationFnData = unknown, | ||||||||||
| TError = DefaultError, | ||||||||||
| TData = void, | ||||||||||
| TMutationKey = unknown, | ||||||||||
| > = OmitKeyof< | ||||||||||
| UseMutationOptions<TMutationFnData, TError, TData, TMutationKey>, | ||||||||||
| 'mutationFn' | ||||||||||
| > & { | ||||||||||
| mutationFn?: Exclude< | ||||||||||
| UseMutationOptions< | ||||||||||
| TMutationFnData, | ||||||||||
| TError, | ||||||||||
| TData, | ||||||||||
| TMutationKey | ||||||||||
| >['mutationFn'], | ||||||||||
| SkipToken | undefined | ||||||||||
| > | ||||||||||
| } | ||||||||||
|
|
||||||||||
| type NonUndefinedGuard<T> = T extends undefined ? never : T | ||||||||||
|
|
||||||||||
| export type DefinedInitialDataOptions< | ||||||||||
| TMutationFnData = unknown, | ||||||||||
| TError = DefaultError, | ||||||||||
| TData = void, | ||||||||||
| TMutationKey = unknown, | ||||||||||
| > = Omit< | ||||||||||
| UseMutationOptions<TMutationFnData, TError, TData, TMutationKey>, | ||||||||||
| 'mutationFn' | ||||||||||
| > & { | ||||||||||
| initialData: | ||||||||||
| | NonUndefinedGuard<TMutationFnData> | ||||||||||
| | (() => NonUndefinedGuard<TMutationFnData>) | ||||||||||
| mutationFn?: MutationFunction<TMutationFnData, TMutationKey> | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export function mutationOptions< | ||||||||||
| TMutationFnData = unknown, | ||||||||||
| TError = DefaultError, | ||||||||||
| TData = void, | ||||||||||
| TMutationKey = unknown, | ||||||||||
| >( | ||||||||||
| options: DefinedInitialDataOptions< | ||||||||||
| TMutationFnData, | ||||||||||
| TError, | ||||||||||
| TData, | ||||||||||
| TMutationKey | ||||||||||
| >, | ||||||||||
| ): DefinedInitialDataOptions<TMutationFnData, TError, TData, TMutationKey> & { | ||||||||||
| mutationKey: DataTag<TMutationKey, TMutationFnData, TError> | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export function mutationOptions< | ||||||||||
| TMutationFnData = unknown, | ||||||||||
| TError = DefaultError, | ||||||||||
| TData = void, | ||||||||||
| TMutationKey = unknown, | ||||||||||
| >( | ||||||||||
| options: UnusedSkipTokenOptions<TMutationFnData, TError, TData, TMutationKey>, | ||||||||||
| ): UnusedSkipTokenOptions<TMutationFnData, TError, TData, TMutationKey> & { | ||||||||||
| mutationKey: DataTag<TMutationKey, TMutationFnData, TError> | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export function mutationOptions< | ||||||||||
| TMutationFnData = unknown, | ||||||||||
| TError = DefaultError, | ||||||||||
| TData = void, | ||||||||||
| TMutationKey = unknown, | ||||||||||
| >( | ||||||||||
| options: UndefinedInitialDataOptions< | ||||||||||
| TMutationFnData, | ||||||||||
| TError, | ||||||||||
| TData, | ||||||||||
| TMutationKey | ||||||||||
| >, | ||||||||||
| ): UndefinedInitialDataOptions<TMutationFnData, TError, TData, TMutationKey> & { | ||||||||||
| mutationKey: DataTag<TMutationKey, TMutationFnData, TError> | ||||||||||
| } | ||||||||||
|
Comment on lines
+60
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant Function OverloadsThe implementation has three function overloads but the actual implementation at line 103 simply returns the options object unchanged. Since all overloads return the same type structure with just different input types, this creates unnecessary complexity without functional differences in behavior. Standards
|
||||||||||
|
|
||||||||||
| export function mutationOptions(options: unknown) { | ||||||||||
| return options | ||||||||||
|
Comment on lines
+103
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent Type HandlingImplementation returns options unmodified but type signatures promise enhanced return with DataTag. Runtime behavior doesn't match type contract, causing potential type inconsistencies.
Suggested change
Standards
|
||||||||||
| } | ||||||||||
|
Comment on lines
+103
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implementation Duplication RiskThe implementation simply returns the input without modification, which could lead to confusion about its purpose. This function appears to be primarily for type inference rather than runtime behavior, which should be clarified. Standards
Comment on lines
+103
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate Implementation LogicThe implementation simply returns the input parameter without modification. This pattern duplicates the implementation style of queryOptions but misses an opportunity for shared abstraction between similar functions, potentially leading to divergent implementations in future maintenance. Standards
Comment on lines
+103
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simplify Implementation LogicThe implementation simply returns the input parameter unchanged, which means the function serves only as a type annotation helper. This pattern could be simplified with a generic identity function or type assertion to reduce code complexity while maintaining the same type safety. Standards
|
||||||||||
Uh oh!
There was an error while loading. Please reload this page.