TypeScript Types
Purpose and Scope
This page collects the public TypeScript surfaces that make SWR feel type-directed rather than merely type-compatible. The central idea is that the request key, fetcher, configuration object, mutation options, preload call, subscription callback, and Suspense mode all participate in inference. When used well, a developer can often write a key and fetcher once and let the hook response, bound mutator, page loader, or trigger function carry the same data and error types through the rest of the component.
SWR’s type model is intentionally broad because SWR keys are broad. A key can be a string, object, tuple, readonly tuple, function that returns a key, or a conditional value such as null or false. The public fetcher types then use conditional types to decide what argument the fetcher receives, or whether a fetcher should be impossible for disabled keys. The type tests show that literal strings, nested records, tuples, readonly tuples, and function keys preserve useful argument information instead of collapsing to an unhelpful any.
Sources: src/_internal/types.ts, test/type/fetcher.ts
The official Basic TypeScript example describes the practical user-facing goal: type the data received from SWR in a normal application. The repository’s type definitions and compile-time tests are the lower-level contract behind that example. They define when data can be undefined, how fallbackData changes the response, how mutation options affect promises, and how pagination and subscriptions specialize the same key-and-fetcher pattern for more advanced data flows.
Relevant Source Files
src/_internal/types.tsdefines shared public and internal types such asFetcherResponse,BareFetcher,Fetcher,CacheData,GlobalState, and the conditionalBlockingDatahelper used to model whether response data can be undefined.src/infinite/types.tsdefinesSWRInfiniteFetcher,SWRInfiniteKeyLoader,SWRInfiniteConfiguration,SWRInfiniteResponse,SWRInfiniteKeyedMutator, and the overloadedSWRInfiniteHooksurface.src/mutation/types.tsdefinesMutationFetcher,SWRMutationConfiguration, trigger overload families, and mutation response typing for theswr/mutationentrypoint.src/subscription/types.tsdefinesSWRSubscriptionOptions,SWRSubscription,SWRSubscriptionResponse, andSWRSubscriptionHookfor theswr/subscriptionentrypoint.test/type/fetcher.tsvalidates fetcher argument inference for strings, conditional keys, records, tuples, readonly tuples, function keys, and infinite page loaders.test/type/config.tsxvalidatesSWRConfig,useSWRConfig,Cache,FullConfiguration, fallback values,cacheData, Suspense, andfallbackDataresponse typing.test/type/mutate.tsvalidates global and bound mutator types, callback data types, filter-key mutation,populateCachebehavior, and promise return shapes.test/type/preload.tsvalidates thepreloadreturn type and fetcher argument inference for literal, tuple, and function keys.test/type/subscription.tsvalidates subscription-specific types and usage against the public subscription hook contract.test/type/suspense/suspense.tsvalidates Suspense-specific response typing in the dedicated Suspense type-test project.
Sources: src/_internal/types.ts, src/infinite/types.ts, src/mutation/types.ts, src/subscription/types.ts, test/type/fetcher.ts, test/type/config.tsx, test/type/mutate.ts, test/type/preload.ts, test/type/subscription.ts, test/type/suspense/suspense.ts
Core Type Surfaces
The shared type surface starts with FetcherResponse<Data>, which is defined as either Data or Promise<Data>. That small union is important because SWR accepts synchronous and asynchronous fetchers throughout the API. BareFetcher<Data> is the permissive form: it accepts any arguments and returns a FetcherResponse<Data>. Fetcher<Data, SWRKey> is the precise form: it inspects the SWR key and turns that key into the fetcher argument type. If the key is a function, the fetcher receives the function’s returned key value; if the key is null, undefined, or false, the fetcher type becomes never.
Sources: src/_internal/types.ts
CacheData<Data> models server-produced cache entries consumed by SWRConfig on the client, with string keys mapped to FetcherResponse<Data>. The same internal file also exposes the shape of GlobalState, a tuple of maps and functions used by the runtime to coordinate revalidators, mutation timestamps, fetch cache, preload cache, scoped mutation, cache setting, and cache subscription. Application code usually does not construct GlobalState, but understanding it helps explain why cache, preload, mutate, and subscription types share vocabulary.
Sources: src/_internal/types.ts, test/type/config.tsx
A compact reference for the shared primitives is:
type FetcherResponse<Data = unknown> = Data | Promise<Data>
type BareFetcher<Data = unknown> = (...args: any[]) => FetcherResponse<Data>
type CacheData<Data = any> = { [key: string]: FetcherResponse<Data> }
type Fetcher<Data = unknown, SWRKey extends Key = Key> = /* key-derived fetcher */These types should be chosen based on how much key safety a wrapper needs. A reusable hook that accepts arbitrary caller fetchers may use BareFetcher, while a domain hook with a known tuple or object key should preserve the key type so that the fetcher receives the same structured argument. The type tests demonstrate this distinction by asserting exact key types inside fetcher callbacks rather than merely asserting the final data type.
Sources: src/_internal/types.ts, test/type/fetcher.ts
Key and Fetcher Inference
SWR’s fetcher inference treats the key as the source of truth. In the type tests, a literal string key makes the fetcher argument that same literal, a conditional string-or-null key removes the disabled branch and still gives the fetcher the string, and a conditional string-or-false key behaves the same. Nested object keys are preserved as structured object types, so a fetcher for { a: '1', b: { c: '3', d: 2 } } receives an object with a as string and b.d as number.
Sources: src/_internal/types.ts, test/type/fetcher.ts
Tuple keys are also part of the public TypeScript contract. The tests cover mutable tuple-like arrays and readonly tuples declared with as const. In the mutable case, SWR preserves the tuple structure while widening values where TypeScript normally widens them, such as a nested array of strings and numbers. In the readonly case, literal values and readonly positions are preserved. This lets fetchers destructure keys safely without losing literal information, which is especially useful for endpoint-plus-parameters patterns.
Sources: test/type/fetcher.ts
Function keys introduce one more layer of inference. The shared Fetcher type checks whether the key is a function returning a usable key or a disabled value. The tests verify that a key function returning a string gives the fetcher a string, while conditional function keys still avoid passing null or false into the fetcher. For infinite loading, SWRInfiniteFetcher<Data, KeyLoader> derives its argument from the return type of the page key loader, and SWRInfiniteKeyLoader receives both the page index and previous page data.
Sources: src/_internal/types.ts, src/infinite/types.ts, test/type/fetcher.ts
Configuration, Fallback Data, and Suspense
Configuration types affect both runtime behavior and response types. SWRConfig accepts undefined, object values, and functions returning configuration objects, while the type tests reject null values and functions returning non-configuration values. The tests also show fallback entries where a key maps to either an immediate fallback value or a promise, and cacheData entries for cache data passed through configuration. useSWRConfig().cache is expected to satisfy the public Cache<any> type, which keeps cache provider usage type-checkable.
Sources: test/type/config.tsx, src/_internal/types.ts
The response type changes when SWR can prove that data is blocking rather than optional. The internal BlockingData conditional type is documented as checking global Suspense, per-hook options, and fallbackData. The type tests confirm this behavior: with { suspense: true }, data can be the concrete fetcher data type; with fallbackData, data can also be narrowed to the inferred data type. However, the tests document a TypeScript limitation: explicitly passing only a partial generic, such as useSWR<string>, can lose config inference, so data may remain string | undefined unless the options generic is also supplied.
Sources: src/_internal/types.ts, test/type/config.tsx, test/type/suspense/suspense.ts
A useful reference pattern is:
useSWR('/api', (key: string) => Promise.resolve(key), { suspense: true })
useSWR('/api', (key: string) => Promise.resolve(key), { fallbackData: 'fallback' })
useSWR<string, any, { suspense: true }>('/api', fetcher, { suspense: true })Use the third form when an explicit data generic is required and the configuration option must still influence the returned data type. Otherwise, prefer letting the fetcher and options drive inference naturally. This is the smoothest path for application code and matches the Basic TypeScript example’s intent of typing the data received from SWR without over-constraining every call.
Sources: test/type/config.tsx, test/type/suspense/suspense.ts
Mutation, Infinite, Subscription, and Preload Types
Mutation types extend the same key-driven model with an extra argument channel. MutationFetcher<Data, SWRKey, ExtraArg> receives the resolved key and a readonly options object containing arg. SWRMutationConfiguration includes revalidate, populateCache, optimisticData, rollbackOnError, fetcher, onSuccess, and onError. The trigger overloads distinguish required arguments, optional arguments, and no-argument triggers, and they refine returned promises based on throwOnError. When throwOnError: false is used, the trigger may resolve to undefined; when throwOnError: true is used, undefined is removed from the data result.
Sources: src/mutation/types.ts
The mutator tests show how this typing appears in normal useSWR and useSWRConfig usage. A bound mutate from useSWR<string> accepts a callback that returns a string or promise of string, and rejects callbacks returning a number even when populateCache: false is supplied. Global mutate can take a key filter function, where the key is typed as Arguments, and can return arrays of mutation results when multiple cache entries match. Generic mutation calls can narrow callback input and promise output to the selected data type.
Sources: test/type/mutate.ts
Infinite types specialize response and mutation for arrays of pages. SWRInfiniteConfiguration adds initialSize, revalidateAll, persistSize, revalidateFirstPage, parallel, an optional typed fetcher, and a page-aware compare. SWRInfiniteResponse<Data, Error> returns data as Data[], exposes size, provides setSize, and replaces the normal mutator with SWRInfiniteKeyedMutator<Data[]>. Its mutation options allow revalidate to be either a boolean or a function that sees an individual page’s data and key.
Sources: src/infinite/types.ts, test/type/fetcher.ts
Subscriptions use a callback contract rather than a fetcher response. SWRSubscriptionOptions provides next, which accepts either an error, data, or a MutatorCallback<Data>. SWRSubscription<SWRSubKey, Data, Error> again derives the subscription key argument from the supplied key and becomes never for disabled keys. The hook returns SWRSubscriptionResponse<Data, Error> with optional data and error, matching the fact that an external source may not have emitted yet.
Sources: src/subscription/types.ts, test/type/subscription.ts
preload is type-tested as an eager fetch pathway with the same key inference rules. A promise-returning fetcher produces a promise of the literal value, while a synchronous fetcher can produce the literal value directly. Tuple keys are passed to the preload fetcher as typed tuples, and function keys preserve literal return types. The tests also note that explicitly specifying the data generic can break the rest-parameter inference, producing the wider FetcherResponse union.
Sources: test/type/preload.ts
Practical Guidance and Testing Signals
For application authors, the most reliable approach is to let TypeScript infer from concrete keys, concrete fetchers, and concrete options. Start with a typed fetcher return value, keep tuple keys as tuples when multiple parameters are needed, and use as const only when literal preservation is required. Add explicit generics when the fetcher is unavailable, when a default global fetcher is used, or when Suspense and fallback options must be reflected in the response type despite TypeScript partial-inference limits.
Sources: test/type/fetcher.ts, test/type/config.tsx, test/type/preload.ts
For library authors wrapping SWR, preserve the key type whenever possible. A wrapper around useSWRInfinite should expose a SWRInfiniteKeyLoader-compatible callback so the fetcher receives the page key type. A wrapper around useSWRMutation should model ExtraArg explicitly so trigger requires the right input. A wrapper around subscriptions should accept a SWRSubscription function instead of a generic callback so disabled keys, emitted data, and emitted errors remain aligned with the public hook contract.
Sources: src/infinite/types.ts, src/mutation/types.ts, src/subscription/types.ts
The type-test files are important maintenance signals. They use compile-time assertions such as expectType, Equal, and @ts-expect-error to lock in both positive and negative behavior. If a change to key serialization, configuration overloads, mutation options, preload signatures, or Suspense response typing compiles but changes these assertions, it is still a public API change for TypeScript users. Contributors should update types and tests together, and should prefer adding a focused type test for every newly supported inference case.
Sources: test/type/fetcher.ts, test/type/config.tsx, test/type/mutate.ts, test/type/preload.ts, test/type/subscription.ts, test/type/suspense/suspense.ts
Related Pages
Read api-use-swr for the primary hook behavior that these types describe at runtime. Read api-swr-config for configuration and cache provider details, api-mutate and api-use-swr-mutation for mutation behavior, api-use-swr-infinite for pagination, api-use-swr-subscription for external data streams, and troubleshooting-suspense-and-server-rendering for the runtime edge cases behind Suspense-specific typing.