Skip to content

feat: Adds ExtraArgument to withTypes for listenerMiddleware. #4517

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/api/createListenerMiddleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -488,11 +488,14 @@ To fix this, the middleware provides types for defining "pre-typed" versions of
import { createListenerMiddleware, addListener } from '@reduxjs/toolkit'
import type { RootState, AppDispatch } from './store'

declare type ExtraArgument = {foo: string};

export const listenerMiddleware = createListenerMiddleware()

export const startAppListening = listenerMiddleware.startListening.withTypes<
RootState,
AppDispatch
AppDispatch,
ExtraArgument
>()

export const addAppListener = addListener.withTypes<RootState, AppDispatch>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type AppThunk<ThunkReturnType = void> = ThunkAction<
unknown,
Action
>
type ExtraArgument = { foo: string }

describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {
const listenerMiddleware = createListenerMiddleware()
Expand All @@ -77,11 +78,12 @@ describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {
test('startListening.withTypes', () => {
const startAppListening = listenerMiddleware.startListening.withTypes<
RootState,
AppDispatch
AppDispatch,
ExtraArgument
>()

expectTypeOf(startAppListening).toEqualTypeOf<
TypedStartListening<RootState, AppDispatch>
TypedStartListening<RootState, AppDispatch, ExtraArgument>
>()

startAppListening({
Expand All @@ -102,6 +104,8 @@ describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {

expectTypeOf(stateCurrent).toEqualTypeOf<RootState>()

expectTypeOf(listenerApi.extra).toEqualTypeOf<ExtraArgument>()

timeout = 1
takeResult = await listenerApi.take(increment.match, timeout)

Expand All @@ -111,10 +115,10 @@ describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {
})

test('addListener.withTypes', () => {
const addAppListener = addListener.withTypes<RootState, AppDispatch>()
const addAppListener = addListener.withTypes<RootState, AppDispatch, ExtraArgument>()

expectTypeOf(addAppListener).toEqualTypeOf<
TypedAddListener<RootState, AppDispatch>
TypedAddListener<RootState, AppDispatch, ExtraArgument>
>()

store.dispatch(
Expand All @@ -126,27 +130,30 @@ describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {
expectTypeOf(state).toEqualTypeOf<RootState>()

expectTypeOf(listenerApi.dispatch).toEqualTypeOf<AppDispatch>()

expectTypeOf(listenerApi.extra).toEqualTypeOf<ExtraArgument>()
},
}),
)
})

test('removeListener.withTypes', () => {
const removeAppListener = removeListener.withTypes<RootState, AppDispatch>()
const removeAppListener = removeListener.withTypes<RootState, AppDispatch, ExtraArgument>()

expectTypeOf(removeAppListener).toEqualTypeOf<
TypedRemoveListener<RootState, AppDispatch>
TypedRemoveListener<RootState, AppDispatch, ExtraArgument>
>()
})

test('stopListening.withTypes', () => {
const stopAppListening = listenerMiddleware.stopListening.withTypes<
RootState,
AppDispatch
AppDispatch,
ExtraArgument
>()

expectTypeOf(stopAppListening).toEqualTypeOf<
TypedStopListening<RootState, AppDispatch>
TypedStopListening<RootState, AppDispatch, ExtraArgument>
>()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,25 @@ type AppThunk<ThunkReturnType = void> = ThunkAction<
Action
>

type ExtraArgument = { foo: string }

const listenerMiddleware = createListenerMiddleware()

const startAppListening = listenerMiddleware.startListening.withTypes<
RootState,
AppDispatch
AppDispatch,
ExtraArgument
>()

const stopAppListening = listenerMiddleware.stopListening.withTypes<
RootState,
AppDispatch
AppDispatch,
ExtraArgument
>()

const addAppListener = addListener.withTypes<RootState, AppDispatch>()
const addAppListener = addListener.withTypes<RootState, AppDispatch, ExtraArgument>()

const removeAppListener = removeListener.withTypes<RootState, AppDispatch>()
const removeAppListener = removeListener.withTypes<RootState, AppDispatch, ExtraArgument>()

describe('startAppListening.withTypes', () => {
test('should return startListening', () => {
Expand Down
84 changes: 54 additions & 30 deletions packages/toolkit/src/listenerMiddleware/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,12 @@ export type RemoveListenerOverloads<
unknown,
UnknownAction
>,
ExtraArgument = unknown,
> = AddListenerOverloads<
boolean,
StateType,
DispatchType,
any,
ExtraArgument,
UnsubscribeListenerOptions
>

Expand Down Expand Up @@ -556,22 +557,23 @@ export type TypedAddListener<
> & {
/**
* Creates a "pre-typed" version of `addListener`
* where the `state` and `dispatch` types are predefined.
* where the `state`, `dispatch` and `extra` types are predefined.
*
* This allows you to set the `state` and `dispatch` types once,
* This allows you to set the `state`, `dispatch` and `extra` types once,
* eliminating the need to specify them with every `addListener` call.
*
* @returns A pre-typed `addListener` with the state and dispatch types already defined.
* @returns A pre-typed `addListener` with the state, dispatch and extra types already defined.
*
* @example
* ```ts
* import { addListener } from '@reduxjs/toolkit'
*
* export const addAppListener = addListener.withTypes<RootState, AppDispatch>()
* export const addAppListener = addListener.withTypes<RootState, AppDispatch, ExtraArguments>()
* ```
*
* @template OverrideStateType - The specific type of state the middleware listener operates on.
* @template OverrideDispatchType - The specific type of the dispatch function.
* @template OverrideExtraArgument - The specific type of the extra object.
*
* @since 2.1.0
*/
Expand All @@ -581,8 +583,9 @@ export type TypedAddListener<
OverrideStateType,
unknown,
UnknownAction
>,
>() => TypedAddListener<OverrideStateType, OverrideDispatchType>
>,
OverrideExtraArgument = unknown,
>() => TypedAddListener<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
}

/**
Expand All @@ -597,34 +600,41 @@ export type TypedRemoveListener<
unknown,
UnknownAction
>,
ExtraArgument = unknown,
Payload = ListenerEntry<StateType, DispatchType>,
T extends string = 'listenerMiddleware/remove',
> = BaseActionCreator<Payload, T> &
AddListenerOverloads<
PayloadAction<Payload, T>,
StateType,
DispatchType,
any,
ExtraArgument,
UnsubscribeListenerOptions
> & {
/**
* Creates a "pre-typed" version of `removeListener`
* where the `state` and `dispatch` types are predefined.
* where the `state`, `dispatch` and `extra` types are predefined.
*
* This allows you to set the `state` and `dispatch` types once,
* This allows you to set the `state`, `dispatch` and `extra` types once,
* eliminating the need to specify them with every `removeListener` call.
*
* @returns A pre-typed `removeListener` with the state and dispatch types already defined.
* @returns A pre-typed `removeListener` with the state, dispatch and extra
* types already defined.
*
* @example
* ```ts
* import { removeListener } from '@reduxjs/toolkit'
*
* export const removeAppListener = removeListener.withTypes<RootState, AppDispatch>()
* export const removeAppListener = removeListener.withTypes<
* RootState,
* AppDispatch,
* ExtraArguments
* >()
* ```
*
* @template OverrideStateType - The specific type of state the middleware listener operates on.
* @template OverrideDispatchType - The specific type of the dispatch function.
* @template OverrideExtraArgument - The specific type of the extra object.
*
* @since 2.1.0
*/
Expand All @@ -635,7 +645,8 @@ export type TypedRemoveListener<
unknown,
UnknownAction
>,
>() => TypedRemoveListener<OverrideStateType, OverrideDispatchType>
OverrideExtraArgument = unknown,
>() => TypedRemoveListener<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
}

/**
Expand All @@ -660,13 +671,13 @@ export type TypedStartListening<
/**
* Creates a "pre-typed" version of
* {@linkcode ListenerMiddlewareInstance.startListening startListening}
* where the `state` and `dispatch` types are predefined.
* where the `state`, `dispatch` and `extra` types are predefined.
*
* This allows you to set the `state` and `dispatch` types once,
* This allows you to set the `state`, `dispatch` and `extra` types once,
* eliminating the need to specify them with every
* {@linkcode ListenerMiddlewareInstance.startListening startListening} call.
*
* @returns A pre-typed `startListening` with the state and dispatch types already defined.
* @returns A pre-typed `startListening` with the state, dispatch and extra types already defined.
*
* @example
* ```ts
Expand All @@ -676,12 +687,14 @@ export type TypedStartListening<
*
* export const startAppListening = listenerMiddleware.startListening.withTypes<
* RootState,
* AppDispatch
* AppDispatch,
* ExtraArguments
* >()
* ```
*
* @template OverrideStateType - The specific type of state the middleware listener operates on.
* @template OverrideDispatchType - The specific type of the dispatch function.
* @template OverrideExtraArgument - The specific type of the extra object.
*
* @since 2.1.0
*/
Expand All @@ -692,7 +705,8 @@ export type TypedStartListening<
unknown,
UnknownAction
>,
>() => TypedStartListening<OverrideStateType, OverrideDispatchType>
OverrideExtraArgument = unknown,
>() => TypedStartListening<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
}

/**
Expand All @@ -707,17 +721,18 @@ export type TypedStopListening<
unknown,
UnknownAction
>,
> = RemoveListenerOverloads<StateType, DispatchType> & {
ExtraArgument = unknown,
> = RemoveListenerOverloads<StateType, DispatchType, ExtraArgument> & {
/**
* Creates a "pre-typed" version of
* {@linkcode ListenerMiddlewareInstance.stopListening stopListening}
* where the `state` and `dispatch` types are predefined.
* where the `state`, `dispatch` and `extra` types are predefined.
*
* This allows you to set the `state` and `dispatch` types once,
* This allows you to set the `state`, `dispatch` and `extra` types once,
* eliminating the need to specify them with every
* {@linkcode ListenerMiddlewareInstance.stopListening stopListening} call.
*
* @returns A pre-typed `stopListening` with the state and dispatch types already defined.
* @returns A pre-typed `stopListening` with the state, dispatch and extra types already defined.
*
* @example
* ```ts
Expand All @@ -727,12 +742,14 @@ export type TypedStopListening<
*
* export const stopAppListening = listenerMiddleware.stopListening.withTypes<
* RootState,
* AppDispatch
* AppDispatch,
* ExtraArguments
* >()
* ```
*
* @template OverrideStateType - The specific type of state the middleware listener operates on.
* @template OverrideDispatchType - The specific type of the dispatch function.
* @template OverrideExtraArgument - The specific type of the extra object.
*
* @since 2.1.0
*/
Expand All @@ -743,7 +760,8 @@ export type TypedStopListening<
unknown,
UnknownAction
>,
>() => TypedStopListening<OverrideStateType, OverrideDispatchType>
OverrideExtraArgument = unknown,
>() => TypedStopListening<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
}

/**
Expand All @@ -758,32 +776,37 @@ export type TypedCreateListenerEntry<
unknown,
UnknownAction
>,
ExtraArgument = unknown,
> = AddListenerOverloads<
ListenerEntry<StateType, DispatchType>,
StateType,
DispatchType
DispatchType,
ExtraArgument
> & {
/**
* Creates a "pre-typed" version of `createListenerEntry`
* where the `state` and `dispatch` types are predefined.
* where the `state`, `dispatch` and `extra` types are predefined.
*
* This allows you to set the `state` and `dispatch` types once, eliminating
* This allows you to set the `state`, `dispatch` and `extra` types once, eliminating
* the need to specify them with every `createListenerEntry` call.
*
* @returns A pre-typed `createListenerEntry` with the state and dispatch types already defined.
* @returns A pre-typed `createListenerEntry` with the state, dispatch and extra
* types already defined.
*
* @example
* ```ts
* import { createListenerEntry } from '@reduxjs/toolkit'
*
* export const createAppListenerEntry = createListenerEntry.withTypes<
* RootState,
* AppDispatch
* AppDispatch,
* ExtraArguments
* >()
* ```
*
* @template OverrideStateType - The specific type of state the middleware listener operates on.
* @template OverrideDispatchType - The specific type of the dispatch function.
* @template OverrideExtraArgument - The specific type of the extra object.
*
* @since 2.1.0
*/
Expand All @@ -794,7 +817,8 @@ export type TypedCreateListenerEntry<
unknown,
UnknownAction
>,
>() => TypedStopListening<OverrideStateType, OverrideDispatchType>
OverrideExtraArgument = unknown,
>() => TypedStopListening<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
}

/**
Expand Down