diff --git a/mutation/types.ts b/mutation/types.ts index 16f221357..c573b5747 100644 --- a/mutation/types.ts +++ b/mutation/types.ts @@ -8,8 +8,8 @@ type FetcherOptions = Readonly<{ export type MutationFetcher< Data = unknown, - ExtraArg = unknown, - SWRKey extends Key = Key + SWRKey extends Key = Key, + ExtraArg = unknown > = SWRKey extends () => infer Arg | null | undefined | false ? (key: Arg, options: FetcherOptions) => FetcherResponse : SWRKey extends null | undefined | false @@ -21,8 +21,8 @@ export type MutationFetcher< export type SWRMutationConfiguration< Data, Error, - ExtraArg = any, SWRMutationKey extends Key = Key, + ExtraArg = any, SWRData = any > = { revalidate?: boolean @@ -31,29 +31,105 @@ export type SWRMutationConfiguration< | ((result: Data, currentData: SWRData | undefined) => SWRData) optimisticData?: SWRData | ((currentData?: SWRData) => SWRData) rollbackOnError?: boolean | ((error: unknown) => boolean) - throwOnError?: boolean - fetcher?: MutationFetcher + fetcher?: MutationFetcher onSuccess?: ( data: Data, key: string, config: Readonly< - SWRMutationConfiguration + SWRMutationConfiguration > ) => void onError?: ( err: Error, key: string, config: Readonly< - SWRMutationConfiguration + SWRMutationConfiguration > ) => void } +type RemoveUndefined = T extends undefined ? never : T +interface TriggerWithArgs< + Data = any, + Error = any, + SWRMutationKey extends Key = Key, + ExtraArg = never +> { + ( + extraArgument: ExtraArg, + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg, + SWRData + > + ): Promise + ( + extraArgument: ExtraArg, + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg, + SWRData + > & { throwOnError: true } + ): Promise> + ( + extraArgument: ExtraArg, + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg, + SWRData + > & { throwOnError: false } + ): Promise +} + +interface TriggerWithoutArgs< + Data = any, + Error = any, + SWRMutationKey extends Key = Key, + ExtraArg = never +> { + ( + extraArgument?: null, + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg, + SWRData + > + ): Promise + ( + extraArgument?: null, + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg, + SWRData + > & { throwOnError: true } + ): Promise> + ( + extraArgument?: null, + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg, + SWRData + > & { throwOnError: false } + ): Promise +} + export interface SWRMutationResponse< Data = any, Error = any, - ExtraArg = never, - SWRMutationKey extends Key = Key + SWRMutationKey extends Key = Key, + ExtraArg = never > extends Pick, 'data' | 'error'> { /** * Indicates if the mutation is in progress. @@ -64,58 +140,95 @@ export interface SWRMutationResponse< * the fetcher, and override the options for the mutation hook. */ trigger: [ExtraArg] extends [never] - ? ( - extraArgument?: null, - options?: SWRMutationConfiguration< - Data, - Error, - ExtraArg, - SWRMutationKey, - SWRData - > - ) => Promise - : ( - extraArgument: ExtraArg, - options?: SWRMutationConfiguration< - Data, - Error, - ExtraArg, - SWRMutationKey, - SWRData - > - ) => Promise + ? TriggerWithoutArgs + : TriggerWithArgs /** * Function to reset the mutation state (`data`, `error`, and `isMutating`). */ reset: () => void } -export type SWRMutationHook = < - Data = any, - Error = any, - SWRMutationKey extends Key = Key, - ExtraArg = never ->( - /** - * The key of the resource that will be mutated. It should be the same key - * used in the `useSWR` hook so SWR can handle revalidation and race - * conditions for that resource. - */ - key: SWRMutationKey, - /** - * The function to trigger the mutation that accepts the key, extra argument - * and options. For example: - * - * ```jsx - * (api, data) => fetch(api, { - * method: 'POST', - * body: JSON.stringify(data) - * }) - * ``` - */ - fetcher: MutationFetcher, - /** - * Extra options for the mutation hook. - */ - options?: SWRMutationConfiguration -) => SWRMutationResponse +export interface SWRMutationHook { + ( + /** + * The key of the resource that will be mutated. It should be the same key + * used in the `useSWR` hook so SWR can handle revalidation and race + * conditions for that resource. + */ + key: SWRMutationKey, + /** + * The function to trigger the mutation that accepts the key, extra argument + * and options. For example: + * + * ```jsx + * (api, data) => fetch(api, { + * method: 'POST', + * body: JSON.stringify(data) + * }) + * ``` + */ + fetcher: MutationFetcher, + /** + * Extra options for the mutation hook. + */ + options?: SWRMutationConfiguration + ): SWRMutationResponse + ( + /** + * The key of the resource that will be mutated. It should be the same key + * used in the `useSWR` hook so SWR can handle revalidation and race + * conditions for that resource. + */ + key: SWRMutationKey, + /** + * The function to trigger the mutation that accepts the key, extra argument + * and options. For example: + * + * ```jsx + * (api, data) => fetch(api, { + * method: 'POST', + * body: JSON.stringify(data) + * }) + * ``` + */ + fetcher: MutationFetcher, + /** + * Extra options for the mutation hook. + */ + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg + > & { throwOnError: false } + ): SWRMutationResponse + ( + /** + * The key of the resource that will be mutated. It should be the same key + * used in the `useSWR` hook so SWR can handle revalidation and race + * conditions for that resource. + */ + key: SWRMutationKey, + /** + * The function to trigger the mutation that accepts the key, extra argument + * and options. For example: + * + * ```jsx + * (api, data) => fetch(api, { + * method: 'POST', + * body: JSON.stringify(data) + * }) + * ``` + */ + fetcher: MutationFetcher, + /** + * Extra options for the mutation hook. + */ + options?: SWRMutationConfiguration< + Data, + Error, + SWRMutationKey, + ExtraArg + > & { throwOnError: true } + ): SWRMutationResponse +} diff --git a/test/type/trigger.ts b/test/type/trigger.ts index 22a4958d1..793a16442 100644 --- a/test/type/trigger.ts +++ b/test/type/trigger.ts @@ -3,7 +3,11 @@ import useSWR from 'swr' type ExpectType = (value: T) => void const expectType: ExpectType = () => {} -type Equal = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; +type Equal = (() => T extends A ? 1 : 2) extends () => T extends B + ? 1 + : 2 + ? true + : false // Test the Equal type expectType>(false) // should be false @@ -25,7 +29,7 @@ export function useTrigger() { // The argument of `trigger` should be number or undefined. expectType[0], number>>(true) - expectType>(trigger(1)) + expectType>(trigger(1)) // Other return values expectType void>>(true) @@ -46,9 +50,36 @@ export function useTriggerWithParameter() { } ) + // The argument of `trigger` should be number or undefined. + expectType[0], number>>(true) + expectType>(trigger(1)) + expectType>( + trigger(1, { + throwOnError: false + }) + ) +} + +export function useOnErrorThrowFalse() { + const { trigger } = useSWRMutation( + '/api/user', + (_, opts) => { + expectType>>(true) + return String(opts.arg) + }, + { + throwOnError: false + } + ) + // The argument of `trigger` should be number or undefined. expectType[0], number>>(true) expectType>(trigger(1)) + expectType>( + trigger(1, { + throwOnError: true + }) + ) } export function useTestSWRMutation() {