Details on how to use various RTK Query APIs with TypeScript
:::
Introduction
As with the rest of the Redux Toolkit package, RTK Query is written in TypeScript, and its API is designed for seamless use in TypeScript applications.
This page provides details for using APIs included in RTK Query with TypeScript and how to type them correctly.
:::info
We strongly recommend using TypeScript 4.1+ with RTK Query for best results.
If you encounter any problems with the types that are not described on this page, please open an issue for discussion.
:::
createApi
Using auto-generated React Hooks
The React-specific entry point for RTK Query exports a version of createApi which automatically generates React hooks for each of the defined query & mutation endpoints.
To use the auto-generated React Hooks as a TypeScript user, you'll need to use TS4.1+.
// file: src/services/types.ts noEmitexporttypePokemon= {}// file: src/services/pokemon.ts// Need to use the React-specific entry point to allow generating React hooksimport { createApi, fetchBaseQuery } from'@reduxjs/toolkit/query/react'importtype { Pokemon } from'./types'// Define a service using a base URL and expected endpointsexportconstpokemonApi=createApi({ reducerPath:'pokemonApi', baseQuery:fetchBaseQuery({ baseUrl:'https://pokeapi.co/api/v2/' }),endpoints: (builder) => ({ getPokemonByName:builder.query<Pokemon,string>({query: (name) =>`pokemon/${name}`, }), }),})// highlight-start// Export hooks for usage in function components, which are// auto-generated based on the defined endpointsexportconst { useGetPokemonByNameQuery } = pokemonApi// highlight-end
For older versions of TS, you can use api.endpoints.[endpointName].useQuery/useMutation to access the same hooks.
The BaseQueryFn type accepts the following generics:
Args - The type for the first parameter of the function. The result returned by a query property on an endpoint will be passed here.
Result - The type to be returned in the data property for the success case. Unless you expect all queries and mutations to return the same type, it is recommended to keep this typed as unknown, and specify the types individually as shown below.
Error - The type to be returned for the error property in the error case. This type also applies to all queryFn functions used in endpoints throughout the API definition.
DefinitionExtraOptions - The type for the third parameter of the function. The value provided to the extraOptions property on an endpoint will be passed here.
Meta - the type of the meta property that may be returned from calling the baseQuery. The meta property is accessible as the second argument to transformResponse.
:::note
The meta property returned from a baseQuery will always be considered as potentially undefined, as a throw in the error case may result in it not being provided. When accessing values from the meta property, this should be accounted for, e.g. using optional chaining
:::
import { createApi, BaseQueryFn } from'@reduxjs/toolkit/query'constsimpleBaseQuery:BaseQueryFn<string,// Argsunknown,// Result { reason:string },// Error { shout?:boolean },// DefinitionExtraOptions { timestamp:number } // Meta> = (arg, api, extraOptions) => {// `arg` has the type `string`// `api` has the type `BaseQueryApi` (not configurable)// `extraOptions` has the type `{ shout?: boolean }constmeta= { timestamp:Date.now() }if (arg ==='forceFail') {return { error: { reason:'Intentionally requested to fail!', meta, }, } }if (extraOptions.shout) {return { data:'CONGRATULATIONS', meta } }return { data:'congratulations', meta }}constapi=createApi({ baseQuery: simpleBaseQuery,endpoints: (builder) => ({ getSupport:builder.query({query: () =>'support me', extraOptions: { shout:true, }, }), }),})
Typing query and mutation endpoints
endpoints for an api are defined as an object using the builder syntax. Both query and mutation endpoints can be typed by providing types to the generics in <ResultType, QueryArg> format.
ResultType - The type of the final data returned by the query, factoring an optional transformResponse.
If transformResponse is not provided, then it is treated as though a successful query will return this type instead.
If transformResponseis provided, the input type for transformResponse must also be specified, to indicate the type that the initial query returns. The return type for transformResponse must match ResultType.
If queryFn is used rather than query, then it must return the following shape for the success case:
{ data: ResultType}
QueryArg - The type of the input that will be passed as the only parameter to the query property of the endpoint, or the first parameter of a queryFn property if used instead.
import { createApi, fetchBaseQuery } from'@reduxjs/toolkit/query/react'interfacePost { id:number name:string}constapi=createApi({ baseQuery:fetchBaseQuery({ baseUrl:'/' }),endpoints: (build) => ({// highlight-start// ResultType QueryArg// v v getPost:build.query<Post,number>({// inferred as `number` from the `QueryArg` type// vquery: (id) =>`post/${id}`,// An explicit type must be provided to the raw result that the query returns// when using `transformResponse`// vtransformResponse: (rawResult: { result: { post:Post } }, meta) => {// ^// The optional `meta` property is available based on the type for the `baseQuery` used// The return value for `transformResponse` must match `ResultType`returnrawResult.result.post }, }),// highlight-end }),})
:::note
queries and mutations can also have their return type defined by a baseQuery rather than the method shown above, however, unless you expect all of your queries and mutations to return the same type, it is recommended to leave the return type of the baseQuery as unknown.
:::
Typing a queryFn
As mentioned in Typing query and mutation endpoints, a queryFn will receive its result & arg types from the generics provided to the corresponding built endpoint.
// file: randomData.ts noEmitexportdeclareconstgetRandomName: () =>string// file: api.tsimport { createApi, fetchBaseQuery } from'@reduxjs/toolkit/query/react'import { getRandomName } from'./randomData'interfacePost { id:number name:string}constapi=createApi({ baseQuery:fetchBaseQuery({ baseUrl:'/' }),endpoints: (build) => ({// highlight-start// ResultType QueryArg// v v getPost:build.query<Post,number>({// inferred as `number` from the `QueryArg` type// vqueryFn: (arg, queryApi, extraOptions, baseQuery) => {constpost:Post= { id: arg, name:getRandomName(), }// For the success case, the return type for the `data` property// must match `ResultType`// vreturn { data: post } }, }),// highlight-end }),})
The error type that a queryFn must return is determined by the baseQuery provided to createApi.
For users who wish to only use queryFn for each endpoint and not include a baseQuery at all, RTK Query provides a fakeBaseQuery function that can be used to easily specify the error type each queryFn should return.
import { createApi, fakeBaseQuery } from'@reduxjs/toolkit/query'// highlight-starttypeCustomErrorType= { reason:'too cold'|'too hot' }// highlight-endconstapi=createApi({// highlight-start// This type will be used as the error type for all `queryFn` functions provided// v baseQuery:fakeBaseQuery<CustomErrorType>(),// highlight-endendpoints: (build) => ({ eatPorridge:build.query<'just right',1|2|3>({// highlight-startqueryFn(seat) {if (seat ===1) {return { error: { reason:'too cold' } } }if (seat ===2) {return { error: { reason:'too hot' } } }return { data:'just right' } },// highlight-end }), microwaveHotPocket:build.query<'delicious!',number>({// highlight-startqueryFn(duration) {if (duration <110) {return { error: { reason:'too cold' } } }if (duration >140) {return { error: { reason:'too hot' } } }return { data:'delicious!' } },// highlight-end }), }),})
Typing providesTags/invalidatesTags
RTK Query utilizes a cache tag invalidation system in order to provide automated re-fetching of stale data.
When using the function notation, both the providesTags and invalidatesTags properties on endpoints are called with the following arguments:
result: ResultType | undefined - The result returned by a successful query. The type corresponds with ResultType as supplied to the built endpoint. In the error case for a query, this will be undefined.
error: ErrorType | undefined - The error returned by an errored query. The type corresponds with Error as supplied to the baseQuery for the api. In the success case for a query, this will be undefined.
arg: QueryArg - The argument supplied to the query property when the query itself is called. The type corresponds with QueryArg as supplied to the built endpoint.
A recommended use-case with providesTags when a query returns a list of items is to provide a tag for each item in the list using the entity ID, as well as a 'LIST' ID tag (see Advanced Invalidation with abstract tag IDs).
This is often written by spreading the result of mapping the received data into an array, as well as an additional item in the array for the 'LIST' ID tag. When spreading the mapped array, by default, TypeScript will broaden the type property to string. As the tag type must correspond to one of the string literals provided to the tagTypes property of the api, the broad string type will not satisfy TypeScript. In order to alleviate this, the tag type can be cast as const to prevent the type being broadened to string.
RTK Query provides the ability to conditionally skip queries from automatically running using the skip parameter as part of query hook options (see Conditional Fetching).
TypeScript users may find that they encounter invalid type scenarios when a query argument is typed to not be undefined, and they attempt to skip the query when an argument would not be valid.
// file: types.ts noEmitexportinterfacePost { id:number name:string}// file: api.tsimport { createApi, fetchBaseQuery } from'@reduxjs/toolkit/query/react'import { Post } from'./types'exportconstapi=createApi({ baseQuery:fetchBaseQuery({ baseUrl:'/' }),endpoints: (build) => ({// Query argument is required to be `number`, and can't be `undefined`// V getPost:build.query<Post,number>({query: (id) =>`post/${id}`, }), }),})exportconst { useGetPostQuery } = api
import { useGetPostQuery } from'./api'functionMaybePost({ id }: { id?:number }) {// This will produce a typescript error:// Argument of type 'number | undefined' is not assignable to parameter of type 'number | unique symbol'.// Type 'undefined' is not assignable to type 'number | unique symbol'.// @ts-expect-error id passed must be a number, but we don't call it when it isn't a numberconst { data } =useGetPostQuery(id, { skip:!id })return <div>...</div>}
While you might be able to convince yourself that the query won't be called unless the id arg is a number at the time, TypeScript won't be convinced so easily.
RTK Query provides a skipToken export which can be used as an alternative to the skip option in order to skip queries, while remaining type-safe. When skipToken is passed as the query argument to useQuery, useQueryState or useQuerySubscription, it provides the same effect as setting skip: true in the query options, while also being a valid argument in scenarios where the arg might be undefined otherwise.
import { skipToken } from'@reduxjs/toolkit/query/react'import { useGetPostQuery } from'./api'functionMaybePost({ id }: { id?:number }) {// When `id` is nullish, we will still skip the query.// TypeScript is also happy that the query will only ever be called with a `number` nowconst { data } =useGetPostQuery(id ?? skipToken)return <div>...</div>}