Skip to content

Commit cebd684

Browse files
committed
[react]: Fix optional reducer action & limit args
1 parent 5eac079 commit cebd684

2 files changed

Lines changed: 19 additions & 5 deletions

File tree

types/react/index.d.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -860,9 +860,10 @@ declare namespace React {
860860
type Dispatch<A> = (value: A) => void;
861861
// Since action _can_ be undefined, dispatch may be called without any parameters.
862862
type DispatchWithoutAction = () => void;
863-
// Get the dispatch type from the reducer arguments
864-
type ActionDispatch<ReducerRest extends any[]> =
865-
ReducerRest extends [any] ? Dispatch<ReducerRest[0]> : DispatchWithoutAction;
863+
// Limit the reducer to accept only 0 or 1 action arguments
864+
type AnyActionArg = [] | [any];
865+
// Get the dispatch type from the reducer arguments (captures optional action argument correctly)
866+
type ActionDispatch<ActionArg extends AnyActionArg> = (...args: ActionArg) => void;
866867
// Unlike redux, the actions _can_ be anything
867868
type Reducer<S, A> = (prevState: S, action: A) => S;
868869
// If useReducer accepts a reducer without action, dispatch may be called without any parameters.
@@ -913,7 +914,7 @@ declare namespace React {
913914
* @version 16.8.0
914915
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
915916
*/
916-
function useReducer<S, A extends any[]>(
917+
function useReducer<S, A extends AnyActionArg>(
917918
reducer: (prevState: S, ...args: A) => S,
918919
initialState: S
919920
): [S, ActionDispatch<A> ];
@@ -927,7 +928,7 @@ declare namespace React {
927928
* @version 16.8.0
928929
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
929930
*/
930-
function useReducer<S, I, A extends any[]>(
931+
function useReducer<S, I, A extends AnyActionArg>(
931932
reducer: (prevState: S, ...args: A) => S,
932933
initialArg: I,
933934
init: (i: I) => S

types/react/test/hooks.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean {
111111
{ name: 'asdf' }
112112
);
113113

114+
// @ts-expect-error reducer should have at most 2 arguments
115+
React.useReducer((v, a: any, b: any) => v, 0);
116+
117+
// useReducer should handle optional action
118+
const reducerWithOptionalAction = (state: boolean, next?: boolean) => {
119+
return next !== undefined ? next : !state;
120+
};
121+
const useToggle = (initialState: boolean) => React.useReducer(reducerWithOptionalAction, initialState);
122+
123+
const [isTooltipVisible, toggleTooltipVisibility] = useToggle(false);
124+
toggleTooltipVisibility();
125+
toggleTooltipVisibility(false);
126+
114127
// inline object, to (manually) check if autocomplete works
115128
React.useReducer(reducer, { age: 42, name: 'The Answer' });
116129

0 commit comments

Comments
 (0)