Skip to content

Commit

Permalink
feat: Adds ExtraArgument to withTypes for listenerMiddleware.
Browse files Browse the repository at this point in the history
These changes are based upon the changes in introduction of withTypes for the listenerMiddleware.
  • Loading branch information
antondalgren committed Jul 17, 2024
1 parent a9362fb commit e1256f3
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 43 deletions.
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

0 comments on commit e1256f3

Please sign in to comment.