diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 68f3549683..5528f267ba 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -21,6 +21,7 @@ // Kanitkorn Sujautra // Sebastian Silbermann // Kyle Scully +// Cong Zhang // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 @@ -822,13 +823,19 @@ declare namespace React { // this technically does accept a second argument, but it's already under a deprecation warning // and it's not even released so probably better to not define it. type Dispatch = (value: A) => void; + // Since action _can_ be undefined, dispatch may be called without any parameters. + type DispatchWithoutAction = () => void; // Unlike redux, the actions _can_ be anything type Reducer = (prevState: S, action: A) => S; + // If useReducer accepts a reducer without action, dispatch may be called without any parameters. + type ReducerWithoutAction = (prevState: S) => S; // types used to try and prevent the compiler from reducing S // to a supertype common with the second argument to useReducer() type ReducerState> = R extends Reducer ? S : never; type ReducerAction> = R extends Reducer ? A : never; // The identity check is done with the SameValue algorithm (Object.is), which is stricter than === + type ReducerStateWithoutAction> = + R extends ReducerWithoutAction ? S : never; // TODO (TypeScript 3.0): ReadonlyArray type DependencyList = ReadonlyArray; @@ -864,6 +871,38 @@ declare namespace React { * @see https://reactjs.org/docs/hooks-reference.html#usestate */ function useState(): [S | undefined, Dispatch>]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + // overload where dispatch could accept 0 arguments. + function useReducer, I>( + reducer: R, + initializerArg: I, + initializer: (arg: I) => ReducerStateWithoutAction + ): [ReducerStateWithoutAction, DispatchWithoutAction]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + // overload where dispatch could accept 0 arguments. + function useReducer>( + reducer: R, + initializerArg: ReducerStateWithoutAction, + initializer?: undefined + ): [ReducerStateWithoutAction, DispatchWithoutAction]; /** * An alternative to `useState`. * diff --git a/types/react/test/hooks.tsx b/types/react/test/hooks.tsx index 74aa17917b..33b6ed9599 100644 --- a/types/react/test/hooks.tsx +++ b/types/react/test/hooks.tsx @@ -97,6 +97,8 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean { // const [reducerState, dispatch] = React.useReducer(reducer, true as true, arg => arg && initialState); const [reducerState, dispatch] = React.useReducer(reducer, true as true, (arg: true): AppState => arg && initialState); + const [, simpleDispatch] = React.useReducer(v => v + 1, 0); + // inline object, to (manually) check if autocomplete works React.useReducer(reducer, { age: 42, name: 'The Answer' }); @@ -158,6 +160,10 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean { }, []); React.useEffect(() => { dispatch({ type: 'getOlder' }); + // $ExpectError + dispatch(); + + simpleDispatch(); setState(reducerState.age); }, []);