# Matching Utilities

Redux Toolkit exports several type-safe action matching utilities that you can leverage when checking for specific kinds of actions. These are primarily useful for the `builder.addMatcher()` cases in `createSlice` and `createReducer`, as well as when writing custom middleware.

#### General Purpose

* `isAllOf` - returns true when **all** conditions are met
* `isAnyOf` - returns true when **at least one of** the conditions are met

#### `createAsyncThunk`-specific matchers

All these matchers can either be called with one or more thunks as arguments, in which case they will return a matcher function for that condition and thunks, or with one actions, in which case they will match for any thunk action with said condition.

* `isAsyncThunkAction` - accepts one or more action creators and returns true when all match
* `isPending` - accepts one or more action creators and returns true when all match
* `isFulfilled` - accepts one or more action creators and returns true when all match
* `isRejected` - accepts one or more action creators and returns true when all match
* `isRejectedWithValue` - accepts one or more action creators and returns true when all match

### `isAllOf`

A higher-order function that accepts one or more of:

* `redux-toolkit` action creator functions such as the ones produced by:
  * [`createAction`](https://app.gitbook.com/s/-MQr6miY_tsYohlEf531/soderzhanie/spravochnik-po-api/prochee/createAction)
  * [`createSlice`](https://app.gitbook.com/s/-MQr6miY_tsYohlEf531/soderzhanie/spravochnik-po-api/prochee/createSlice#return-value)
  * [`createAsyncThunk`](https://app.gitbook.com/s/-MQr6miY_tsYohlEf531/soderzhanie/spravochnik-po-api/prochee/createAsyncThunk#promise-lifecycle-actions)
* type guard functions
* custom action creator functions that have a `.match` property that is a type guard

It will return a type guard function that returns `true` if *all* of the provided functions match.

### `isAnyOf`

Accepts the same inputs as `isAllOf` and will return a type guard function that returns `true` if at least one of the provided functions match.

### `isAsyncThunkAction`

A higher-order function that returns a type guard function that may be used to check whether an action was created by [`createAsyncThunk`](https://app.gitbook.com/s/-MQr6miY_tsYohlEf531/soderzhanie/spravochnik-po-api/prochee/createAsyncThunk).

\`\`\`ts title="isAsyncThunkAction usage" import { isAsyncThunkAction, AnyAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isARequestAction = isAsyncThunkAction(requestThunk1, requestThunk2)

function handleRequestAction(action: AnyAction) { if (isARequestAction(action)) { // action is an action dispatched by either `requestThunk1` or `requestThunk2` } }

````
## `isPending`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'pending' action creator from the `createAsyncThunk` promise lifecycle.

```ts title="isPending usage"
import { isPending, AnyAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isAPendingAction = isPending(requestThunk1, requestThunk2)

function handlePendingAction(action: AnyAction) {
  if (isAPendingAction(action)) {
    // action is a pending action dispatched by either `requestThunk1` or `requestThunk2`
  }
}
````

### `isFulfilled`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'fulfilled'' action creator from the `createAsyncThunk` promise lifecycle.

\`\`\`ts title="isFulfilled usage" import { isFulfilled, AnyAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isAFulfilledAction = isFulfilled(requestThunk1, requestThunk2)

function handleFulfilledAction(action: AnyAction) { if (isAFulfilledAction(action)) { // action is a fulfilled action dispatched by either `requestThunk1` or `requestThunk2` } }

````
## `isRejected`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle.

```ts title="isRejected usage"
import { isRejected, AnyAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isARejectedAction = isRejected(requestThunk1, requestThunk2)

function handleRejectedAction(action: AnyAction) {
  if (isARejectedAction(action)) {
    // action is a rejected action dispatched by either `requestThunk1` or `requestThunk2`
  }
}
````

### `isRejectedWithValue`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle that was created by [`rejectWithValue`](https://app.gitbook.com/s/-MQr6miY_tsYohlEf531/soderzhanie/spravochnik-po-api/prochee/createAsyncThunk#handling-thunk-errors).

\`\`\`ts title="isRejectedWithValue usage" import { isRejectedWithValue, AnyAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isARejectedWithValueAction = isRejectedWithValue( requestThunk1, requestThunk2 )

function handleRejectedWithValueAction(action: AnyAction) { if (isARejectedWithValueAction(action)) { // action is a rejected action dispatched by either `requestThunk1` or `requestThunk2` // where rejectWithValue was used } }

````
## Using matchers to reduce code complexity, duplication and boilerplate

When using the `builder` pattern to construct a reducer, we add cases or matchers one at a time. However, by using `isAnyOf` or `isAllOf`,
we're able to easily use the same matcher for several cases in a type-safe manner.

First, let's examine an unnecessarily complex example:

```ts title="Example without using a matcher utility"
import {
  createAsyncThunk,
  createReducer,
  PayloadAction
} from '@reduxjs/toolkit'

interface Data {
  isInteresting: boolean
  isSpecial: boolean
}

interface Special extends Data {
  isSpecial: true
}

interface Interesting extends Data {
  isInteresting: true
}

function isSpecial(
  action: PayloadAction<Data>
): action is PayloadAction<Special> {
  return action.payload.isSpecial
}

function isInteresting(
  action: PayloadAction<Data>
): action is PayloadAction<Interesting> {
  return action.payload.isInteresting
}

interface ExampleState {
  isSpecial: boolean
  isInteresting: boolean
}

const initialState: ExampleState = {
  isSpecial: false,
  isInteresting: false
}

export const isSpecialAndInterestingThunk = createAsyncThunk(
  'isSpecialAndInterestingThunk',
  () => {
    return {
      isSpecial: true,
      isInteresting: true
    }
  }
)

// This has unnecessary complexity
const loadingReducer = createReducer(initialState, builder => {
  builder.addCase(isSpecialAndInterestingThunk.fulfilled, (state, action) => {
    if (isSpecial(action)) {
      state.isSpecial = true
    }
    if (isInteresting(action)) {
      state.isInteresting = true
    }
  })
})
````

In this scenario, we can use `isAllOf` to simplify our code and reduce some of the boilerplate.

\`\`\`ts title="Refactoring with isAllOf" import { createReducer, isAllOf } from '@reduxjs/toolkit' import { isSpecialAndInterestingThunk, initialState, isSpecial, isInteresting, Data } from '@virtual/matchers' // This is a fake pkg that provides the types shown above

const loadingReducer = createReducer(initialState, builder => { builder .addMatcher( isAllOf(isSpecialAndInterestingThunk.fulfilled, isSpecial), (state, action) => { state.isSpecial = true } ) .addMatcher( isAllOf(isSpecialAndInterestingThunk.fulfilled, isInteresting), (state, action) => { state.isInteresting = true } ) })

````
## Using matchers as a TypeScript Type Guard

The function returned by `isAllOf` and `isAnyOf` can also be used as a TypeScript type guard in other contexts.

```ts title="Using isAllOf as a type guard"
import { isAllOf, PayloadAction } from '@reduxjs/toolkit'
import { Data, isSpecial, isInteresting } from '@virtual/matchers' // This is a fake pkg that provides the types shown above

const isSpecialAndInteresting = isAllOf(isSpecial, isInteresting)

function someFunction(action: PayloadAction<Data>) {
  if (isSpecialAndInteresting(action)) {
    // "action" will be correctly typed as:
    // `PayloadAction<Special> & PayloadAction<Interesting>`
  }
}
````

\`\`\`ts title="Using isAnyOf as a type guard" import { isAnyOf, PayloadAction } from '@reduxjs/toolkit' import { Data, isSpecial, isInteresting } from '@virtual/matchers' // this is a fake pkg that provides the types shown above

const isSpecialOrInteresting = isAnyOf(isSpecial, isInteresting)

function someFunction(action: PayloadAction) { if (isSpecialOrInteresting(action)) { // "action" will be correctly typed as: // `PayloadAction<Special> | PayloadAction<Interesting>` } }

\`\`\`

### Example

\<iframe src="<https://codesandbox.io/embed/redux-toolkit-matchers-example-e765q?fontsize=14&hidenavigation=1&module=%2Fsrc%2Ffeatures%2Fcounter%2FcounterSlice.ts&theme=dark>" style= title="redux-toolkit-matchers-example" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"

> \</iframe>
