types.ts


Overview

types.ts defines the TypeScript type declarations and interfaces that underpin the Remote Data Mutation functionality within the SWR ecosystem. This file provides a comprehensive and strongly-typed contract for mutation fetchers, mutation hook configurations, trigger functions, and mutation hook responses.

The types in this file enable flexible, type-safe remote mutations in React applications using SWR by specifying:

By isolating these mutation-related types, the system ensures consistent, extensible, and safe usage of mutation hooks across the codebase and by consumers.


Detailed Type Explanations

1. FetcherResponse<Data>

type FetcherResponse<Data> = Data | Promise<Data>

2. FetcherOptions<ExtraArg = unknown>

type FetcherOptions<ExtraArg = unknown> = Readonly<{
  arg: ExtraArg
}>

3. MutationFetcher

export type MutationFetcher<
  Data = unknown,
  SWRKey extends Key = Key,
  ExtraArg = unknown
> = SWRKey extends () => infer Arg | null | undefined | false
  ? (key: Arg, options: FetcherOptions<ExtraArg>) => FetcherResponse<Data>
  : SWRKey extends null | undefined | false
  ? never
  : SWRKey extends infer Arg
  ? (key: Arg, options: FetcherOptions<ExtraArg>) => FetcherResponse<Data>
  : never
const fetcher: MutationFetcher<UserData, string, FormInput> = (key, { arg }) => {
  return fetch(key, {
    method: 'POST',
    body: JSON.stringify(arg),
  }).then(res => res.json());
};

4. SWRMutationConfiguration

export type SWRMutationConfiguration<
  Data,
  Error,
  SWRMutationKey extends Key = Key,
  ExtraArg = any,
  SWRData = any
> = { ... }
const config: SWRMutationConfiguration<User, Error, string, FormData, User> = {
  revalidate: true,
  optimisticData: currentUser => ({ ...currentUser, name: "Loading..." }),
  rollbackOnError: true,
  onSuccess: (data) => console.log('Mutation succeeded:', data),
  onError: (error) => console.error('Mutation failed:', error),
};

5. Utility Types: RemoveUndefined and IsUndefinedIncluded

type RemoveUndefined<T> = T extends undefined ? never : T
type IsUndefinedIncluded<T> = undefined extends T ? true : false

6. Trigger Function Interfaces

These interfaces define multiple overloads for the trigger function used to perform mutations with varying argument presence and error handling.

a. TriggerWithArgs

export interface TriggerWithArgs<
  Data = any,
  Error = any,
  SWRMutationKey extends Key = Key,
  ExtraArg = never
> {
  <SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<...>): Promise<Data>
  <SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<...> & { throwOnError: true }): Promise<RemoveUndefined<Data>>
  <SWRData = Data>(extraArgument: ExtraArg, options?: SWRMutationConfiguration<...> & { throwOnError: false }): Promise<Data | undefined>
}
await trigger({ name: 'Alice' }, { throwOnError: true });

b. TriggerWithOptionsArgs

export interface TriggerWithOptionsArgs<...> { ... }
await trigger(undefined, options);
await trigger({ id: 123 });

c. TriggerWithoutArgs

export interface TriggerWithoutArgs<...>{ ... }
await trigger();
await trigger(null);

7. SWRMutationResponse

export interface SWRMutationResponse<
  Data = any,
  Error = any,
  SWRMutationKey extends Key = Key,
  ExtraArg = never
> extends Pick<SWRResponse<Data, Error>, 'data' | 'error'> {
  isMutating: boolean
  trigger: /* one of TriggerWithArgs, TriggerWithOptionsArgs, TriggerWithoutArgs based on ExtraArg */
  reset: () => void
}
const { data, error, isMutating, trigger, reset } = useSWRMutation(...);

await trigger(extraArg, options);
reset();

8. SWRMutationHook

export interface SWRMutationHook {
  <
    Data = any,
    Error = any,
    SWRMutationKey extends Key = Key,
    ExtraArg = never,
    SWRData = Data
  >(
    key: SWRMutationKey,
    fetcher: MutationFetcher<Data, SWRMutationKey, ExtraArg>,
    options?: SWRMutationConfiguration<Data, Error, SWRMutationKey, ExtraArg, SWRData> & { throwOnError?: boolean }
  ): SWRMutationResponse<Data, Error, SWRMutationKey, ExtraArg>
  // Overloads with throwOnError true/false for refined return types
}
const mutation = useSWRMutation('/api/user', fetcher, { optimisticData: {...} });

Important Implementation Details


Interaction with Other Parts of the System


Usage Examples

Defining a Mutation Fetcher

type UserData = { id: number; name: string };
type UpdateUserPayload = { name: string };

const updateUserFetcher: MutationFetcher<UserData, string, UpdateUserPayload> = (key, { arg }) => {
  return fetch(key, {
    method: 'PUT',
    body: JSON.stringify(arg),
  }).then(res => res.json());
};

Using the Mutation Hook

const { data, error, isMutating, trigger, reset } = useSWRMutation(
  '/api/user/123',
  updateUserFetcher,
  {
    optimisticData: { id: 123, name: 'Loading...' },
    rollbackOnError: true,
    onSuccess: (data) => console.log('User updated:', data),
  }
);

await trigger({ name: 'Alice' }, { throwOnError: true });

Mermaid Class Diagram

classDiagram
    class MutationFetcher {
      <<type>>
      + (key: Arg, options: FetcherOptions) => Data | Promise<Data>
    }

    class SWRMutationConfiguration {
      + revalidate: boolean | function
      + populateCache: boolean | function
      + optimisticData: any | function
      + rollbackOnError: boolean | function
      + fetcher: MutationFetcher
      + onSuccess(data, key, config): void
      + onError(error, key, config): void
    }

    class TriggerFunction {
      <<interface>>
      + (extraArgument?, options?): Promise<Data | undefined>
    }

    class SWRMutationResponse {
      + data
      + error
      + isMutating: boolean
      + trigger: TriggerFunction
      + reset(): void
    }

    class SWRMutationHook {
      <<function>>
      + (key, fetcher, options?): SWRMutationResponse
    }

    MutationFetcher <|-- SWRMutationConfiguration : uses
    SWRMutationConfiguration <-- TriggerFunction : uses
    TriggerFunction <-- SWRMutationResponse : exposes
    SWRMutationResponse <-- SWRMutationHook : returns

Summary

The types.ts file encapsulates the essential TypeScript types for SWR's remote mutation feature, enabling:

This type layer is critical for building robust, type-safe mutation hooks that developers can confidently use to perform remote data mutations with predictable semantics and rich feature support.