Skip to content

Commit ae1dcbf

Browse files
authored
Merge pull request #4517 from antondalgren/add-extra-args-to-listener-middleware-with-types
2 parents e33130e + e1256f3 commit ae1dcbf

File tree

4 files changed

+81
-43
lines changed

4 files changed

+81
-43
lines changed

docs/api/createListenerMiddleware.mdx

+4-1
Original file line numberDiff line numberDiff line change
@@ -488,11 +488,14 @@ To fix this, the middleware provides types for defining "pre-typed" versions of
488488
import { createListenerMiddleware, addListener } from '@reduxjs/toolkit'
489489
import type { RootState, AppDispatch } from './store'
490490

491+
declare type ExtraArgument = {foo: string};
492+
491493
export const listenerMiddleware = createListenerMiddleware()
492494

493495
export const startAppListening = listenerMiddleware.startListening.withTypes<
494496
RootState,
495-
AppDispatch
497+
AppDispatch,
498+
ExtraArgument
496499
>()
497500

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

packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.withTypes.test-d.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type AppThunk<ThunkReturnType = void> = ThunkAction<
6464
unknown,
6565
Action
6666
>
67+
type ExtraArgument = { foo: string }
6768

6869
describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {
6970
const listenerMiddleware = createListenerMiddleware()
@@ -77,11 +78,12 @@ describe('listenerMiddleware.withTypes<RootState, AppDispatch>()', () => {
7778
test('startListening.withTypes', () => {
7879
const startAppListening = listenerMiddleware.startListening.withTypes<
7980
RootState,
80-
AppDispatch
81+
AppDispatch,
82+
ExtraArgument
8183
>()
8284

8385
expectTypeOf(startAppListening).toEqualTypeOf<
84-
TypedStartListening<RootState, AppDispatch>
86+
TypedStartListening<RootState, AppDispatch, ExtraArgument>
8587
>()
8688

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

103105
expectTypeOf(stateCurrent).toEqualTypeOf<RootState>()
104106

107+
expectTypeOf(listenerApi.extra).toEqualTypeOf<ExtraArgument>()
108+
105109
timeout = 1
106110
takeResult = await listenerApi.take(increment.match, timeout)
107111

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

113117
test('addListener.withTypes', () => {
114-
const addAppListener = addListener.withTypes<RootState, AppDispatch>()
118+
const addAppListener = addListener.withTypes<RootState, AppDispatch, ExtraArgument>()
115119

116120
expectTypeOf(addAppListener).toEqualTypeOf<
117-
TypedAddListener<RootState, AppDispatch>
121+
TypedAddListener<RootState, AppDispatch, ExtraArgument>
118122
>()
119123

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

128132
expectTypeOf(listenerApi.dispatch).toEqualTypeOf<AppDispatch>()
133+
134+
expectTypeOf(listenerApi.extra).toEqualTypeOf<ExtraArgument>()
129135
},
130136
}),
131137
)
132138
})
133139

134140
test('removeListener.withTypes', () => {
135-
const removeAppListener = removeListener.withTypes<RootState, AppDispatch>()
141+
const removeAppListener = removeListener.withTypes<RootState, AppDispatch, ExtraArgument>()
136142

137143
expectTypeOf(removeAppListener).toEqualTypeOf<
138-
TypedRemoveListener<RootState, AppDispatch>
144+
TypedRemoveListener<RootState, AppDispatch, ExtraArgument>
139145
>()
140146
})
141147

142148
test('stopListening.withTypes', () => {
143149
const stopAppListening = listenerMiddleware.stopListening.withTypes<
144150
RootState,
145-
AppDispatch
151+
AppDispatch,
152+
ExtraArgument
146153
>()
147154

148155
expectTypeOf(stopAppListening).toEqualTypeOf<
149-
TypedStopListening<RootState, AppDispatch>
156+
TypedStopListening<RootState, AppDispatch, ExtraArgument>
150157
>()
151158
})
152159
})

packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.withTypes.test.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,25 @@ type AppThunk<ThunkReturnType = void> = ThunkAction<
5555
Action
5656
>
5757

58+
type ExtraArgument = { foo: string }
59+
5860
const listenerMiddleware = createListenerMiddleware()
5961

6062
const startAppListening = listenerMiddleware.startListening.withTypes<
6163
RootState,
62-
AppDispatch
64+
AppDispatch,
65+
ExtraArgument
6366
>()
6467

6568
const stopAppListening = listenerMiddleware.stopListening.withTypes<
6669
RootState,
67-
AppDispatch
70+
AppDispatch,
71+
ExtraArgument
6872
>()
6973

70-
const addAppListener = addListener.withTypes<RootState, AppDispatch>()
74+
const addAppListener = addListener.withTypes<RootState, AppDispatch, ExtraArgument>()
7175

72-
const removeAppListener = removeListener.withTypes<RootState, AppDispatch>()
76+
const removeAppListener = removeListener.withTypes<RootState, AppDispatch, ExtraArgument>()
7377

7478
describe('startAppListening.withTypes', () => {
7579
test('should return startListening', () => {

packages/toolkit/src/listenerMiddleware/types.ts

+54-30
Original file line numberDiff line numberDiff line change
@@ -506,11 +506,12 @@ export type RemoveListenerOverloads<
506506
unknown,
507507
UnknownAction
508508
>,
509+
ExtraArgument = unknown,
509510
> = AddListenerOverloads<
510511
boolean,
511512
StateType,
512513
DispatchType,
513-
any,
514+
ExtraArgument,
514515
UnsubscribeListenerOptions
515516
>
516517

@@ -551,22 +552,23 @@ export type TypedAddListener<
551552
> & {
552553
/**
553554
* Creates a "pre-typed" version of `addListener`
554-
* where the `state` and `dispatch` types are predefined.
555+
* where the `state`, `dispatch` and `extra` types are predefined.
555556
*
556-
* This allows you to set the `state` and `dispatch` types once,
557+
* This allows you to set the `state`, `dispatch` and `extra` types once,
557558
* eliminating the need to specify them with every `addListener` call.
558559
*
559-
* @returns A pre-typed `addListener` with the state and dispatch types already defined.
560+
* @returns A pre-typed `addListener` with the state, dispatch and extra types already defined.
560561
*
561562
* @example
562563
* ```ts
563564
* import { addListener } from '@reduxjs/toolkit'
564565
*
565-
* export const addAppListener = addListener.withTypes<RootState, AppDispatch>()
566+
* export const addAppListener = addListener.withTypes<RootState, AppDispatch, ExtraArguments>()
566567
* ```
567568
*
568569
* @template OverrideStateType - The specific type of state the middleware listener operates on.
569570
* @template OverrideDispatchType - The specific type of the dispatch function.
571+
* @template OverrideExtraArgument - The specific type of the extra object.
570572
*
571573
* @since 2.1.0
572574
*/
@@ -576,8 +578,9 @@ export type TypedAddListener<
576578
OverrideStateType,
577579
unknown,
578580
UnknownAction
579-
>,
580-
>() => TypedAddListener<OverrideStateType, OverrideDispatchType>
581+
>,
582+
OverrideExtraArgument = unknown,
583+
>() => TypedAddListener<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
581584
}
582585

583586
/**
@@ -592,34 +595,41 @@ export type TypedRemoveListener<
592595
unknown,
593596
UnknownAction
594597
>,
598+
ExtraArgument = unknown,
595599
Payload = ListenerEntry<StateType, DispatchType>,
596600
T extends string = 'listenerMiddleware/remove',
597601
> = BaseActionCreator<Payload, T> &
598602
AddListenerOverloads<
599603
PayloadAction<Payload, T>,
600604
StateType,
601605
DispatchType,
602-
any,
606+
ExtraArgument,
603607
UnsubscribeListenerOptions
604608
> & {
605609
/**
606610
* Creates a "pre-typed" version of `removeListener`
607-
* where the `state` and `dispatch` types are predefined.
611+
* where the `state`, `dispatch` and `extra` types are predefined.
608612
*
609-
* This allows you to set the `state` and `dispatch` types once,
613+
* This allows you to set the `state`, `dispatch` and `extra` types once,
610614
* eliminating the need to specify them with every `removeListener` call.
611615
*
612-
* @returns A pre-typed `removeListener` with the state and dispatch types already defined.
616+
* @returns A pre-typed `removeListener` with the state, dispatch and extra
617+
* types already defined.
613618
*
614619
* @example
615620
* ```ts
616621
* import { removeListener } from '@reduxjs/toolkit'
617622
*
618-
* export const removeAppListener = removeListener.withTypes<RootState, AppDispatch>()
623+
* export const removeAppListener = removeListener.withTypes<
624+
* RootState,
625+
* AppDispatch,
626+
* ExtraArguments
627+
* >()
619628
* ```
620629
*
621630
* @template OverrideStateType - The specific type of state the middleware listener operates on.
622631
* @template OverrideDispatchType - The specific type of the dispatch function.
632+
* @template OverrideExtraArgument - The specific type of the extra object.
623633
*
624634
* @since 2.1.0
625635
*/
@@ -630,7 +640,8 @@ export type TypedRemoveListener<
630640
unknown,
631641
UnknownAction
632642
>,
633-
>() => TypedRemoveListener<OverrideStateType, OverrideDispatchType>
643+
OverrideExtraArgument = unknown,
644+
>() => TypedRemoveListener<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
634645
}
635646

636647
/**
@@ -655,13 +666,13 @@ export type TypedStartListening<
655666
/**
656667
* Creates a "pre-typed" version of
657668
* {@linkcode ListenerMiddlewareInstance.startListening startListening}
658-
* where the `state` and `dispatch` types are predefined.
669+
* where the `state`, `dispatch` and `extra` types are predefined.
659670
*
660-
* This allows you to set the `state` and `dispatch` types once,
671+
* This allows you to set the `state`, `dispatch` and `extra` types once,
661672
* eliminating the need to specify them with every
662673
* {@linkcode ListenerMiddlewareInstance.startListening startListening} call.
663674
*
664-
* @returns A pre-typed `startListening` with the state and dispatch types already defined.
675+
* @returns A pre-typed `startListening` with the state, dispatch and extra types already defined.
665676
*
666677
* @example
667678
* ```ts
@@ -671,12 +682,14 @@ export type TypedStartListening<
671682
*
672683
* export const startAppListening = listenerMiddleware.startListening.withTypes<
673684
* RootState,
674-
* AppDispatch
685+
* AppDispatch,
686+
* ExtraArguments
675687
* >()
676688
* ```
677689
*
678690
* @template OverrideStateType - The specific type of state the middleware listener operates on.
679691
* @template OverrideDispatchType - The specific type of the dispatch function.
692+
* @template OverrideExtraArgument - The specific type of the extra object.
680693
*
681694
* @since 2.1.0
682695
*/
@@ -687,7 +700,8 @@ export type TypedStartListening<
687700
unknown,
688701
UnknownAction
689702
>,
690-
>() => TypedStartListening<OverrideStateType, OverrideDispatchType>
703+
OverrideExtraArgument = unknown,
704+
>() => TypedStartListening<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
691705
}
692706

693707
/**
@@ -702,17 +716,18 @@ export type TypedStopListening<
702716
unknown,
703717
UnknownAction
704718
>,
705-
> = RemoveListenerOverloads<StateType, DispatchType> & {
719+
ExtraArgument = unknown,
720+
> = RemoveListenerOverloads<StateType, DispatchType, ExtraArgument> & {
706721
/**
707722
* Creates a "pre-typed" version of
708723
* {@linkcode ListenerMiddlewareInstance.stopListening stopListening}
709-
* where the `state` and `dispatch` types are predefined.
724+
* where the `state`, `dispatch` and `extra` types are predefined.
710725
*
711-
* This allows you to set the `state` and `dispatch` types once,
726+
* This allows you to set the `state`, `dispatch` and `extra` types once,
712727
* eliminating the need to specify them with every
713728
* {@linkcode ListenerMiddlewareInstance.stopListening stopListening} call.
714729
*
715-
* @returns A pre-typed `stopListening` with the state and dispatch types already defined.
730+
* @returns A pre-typed `stopListening` with the state, dispatch and extra types already defined.
716731
*
717732
* @example
718733
* ```ts
@@ -722,12 +737,14 @@ export type TypedStopListening<
722737
*
723738
* export const stopAppListening = listenerMiddleware.stopListening.withTypes<
724739
* RootState,
725-
* AppDispatch
740+
* AppDispatch,
741+
* ExtraArguments
726742
* >()
727743
* ```
728744
*
729745
* @template OverrideStateType - The specific type of state the middleware listener operates on.
730746
* @template OverrideDispatchType - The specific type of the dispatch function.
747+
* @template OverrideExtraArgument - The specific type of the extra object.
731748
*
732749
* @since 2.1.0
733750
*/
@@ -738,7 +755,8 @@ export type TypedStopListening<
738755
unknown,
739756
UnknownAction
740757
>,
741-
>() => TypedStopListening<OverrideStateType, OverrideDispatchType>
758+
OverrideExtraArgument = unknown,
759+
>() => TypedStopListening<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
742760
}
743761

744762
/**
@@ -753,32 +771,37 @@ export type TypedCreateListenerEntry<
753771
unknown,
754772
UnknownAction
755773
>,
774+
ExtraArgument = unknown,
756775
> = AddListenerOverloads<
757776
ListenerEntry<StateType, DispatchType>,
758777
StateType,
759-
DispatchType
778+
DispatchType,
779+
ExtraArgument
760780
> & {
761781
/**
762782
* Creates a "pre-typed" version of `createListenerEntry`
763-
* where the `state` and `dispatch` types are predefined.
783+
* where the `state`, `dispatch` and `extra` types are predefined.
764784
*
765-
* This allows you to set the `state` and `dispatch` types once, eliminating
785+
* This allows you to set the `state`, `dispatch` and `extra` types once, eliminating
766786
* the need to specify them with every `createListenerEntry` call.
767787
*
768-
* @returns A pre-typed `createListenerEntry` with the state and dispatch types already defined.
788+
* @returns A pre-typed `createListenerEntry` with the state, dispatch and extra
789+
* types already defined.
769790
*
770791
* @example
771792
* ```ts
772793
* import { createListenerEntry } from '@reduxjs/toolkit'
773794
*
774795
* export const createAppListenerEntry = createListenerEntry.withTypes<
775796
* RootState,
776-
* AppDispatch
797+
* AppDispatch,
798+
* ExtraArguments
777799
* >()
778800
* ```
779801
*
780802
* @template OverrideStateType - The specific type of state the middleware listener operates on.
781803
* @template OverrideDispatchType - The specific type of the dispatch function.
804+
* @template OverrideExtraArgument - The specific type of the extra object.
782805
*
783806
* @since 2.1.0
784807
*/
@@ -789,7 +812,8 @@ export type TypedCreateListenerEntry<
789812
unknown,
790813
UnknownAction
791814
>,
792-
>() => TypedStopListening<OverrideStateType, OverrideDispatchType>
815+
OverrideExtraArgument = unknown,
816+
>() => TypedStopListening<OverrideStateType, OverrideDispatchType, OverrideExtraArgument>
793817
}
794818

795819
/**

0 commit comments

Comments
 (0)