Official React bindings for Redux state management with hooks and higher-order components
—
React Redux provides comprehensive TypeScript support with full type safety, pre-typed hooks, and extensive type definitions for all API surfaces.
React Redux 9.1.0+ introduces .withTypes() methods for creating pre-typed hooks, eliminating the need to type state and dispatch on every usage.
/**
* Creates a pre-typed useSelector hook
* @returns UseSelector hook bound to specific state type
*/
interface UseSelector<StateType = unknown> {
withTypes: <OverrideStateType extends StateType>() => UseSelector<OverrideStateType>;
}
/**
* Creates a pre-typed useDispatch hook
* @returns UseDispatch hook bound to specific dispatch type
*/
interface UseDispatch<DispatchType extends Dispatch<UnknownAction> = Dispatch<UnknownAction>> {
withTypes: <OverrideDispatchType extends DispatchType>() => UseDispatch<OverrideDispatchType>;
}
/**
* Creates a pre-typed useStore hook
* @returns UseStore hook bound to specific store type
*/
interface UseStore<StoreType extends Store> {
withTypes: <OverrideStoreType extends StoreType>() => UseStore<OverrideStoreType>;
}Setup and Usage:
import { useSelector, useDispatch, useStore } from "react-redux";
import type { RootState, AppDispatch, AppStore } from "./store";
// Create pre-typed hooks
export const useAppSelector = useSelector.withTypes<RootState>();
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppStore = useStore.withTypes<AppStore>();
// Use throughout your app with full type safety
function TodoComponent() {
// state parameter is automatically typed as RootState
const todos = useAppSelector((state) => state.todos);
// dispatch is automatically typed as AppDispatch (with thunk support)
const dispatch = useAppDispatch();
// store is automatically typed as AppStore
const store = useAppStore();
return <div>{todos.length} todos</div>;
}Legacy typed hook interface for pre-9.1.0 versions or alternative typing approaches.
/**
* Legacy interface for typed useSelector hook
*/
interface TypedUseSelectorHook<TState> {
<TSelected>(
selector: (state: TState) => TSelected,
equalityFn?: EqualityFn<NoInfer<TSelected>>
): TSelected;
<Selected = unknown>(
selector: (state: TState) => Selected,
options?: UseSelectorOptions<Selected>
): Selected;
}
type NoInfer<T> = [T][T extends any ? 0 : never];Legacy Usage:
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
// Create typed hooks (legacy approach)
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppDispatch = () => useDispatch<AppDispatch>();Define your store types for use with pre-typed hooks.
import { configureStore } from "@reduxjs/toolkit";
import { useDispatch, useSelector, useStore } from "react-redux";
const store = configureStore({
reducer: {
todos: todosReducer,
user: userReducer
}
});
// Infer types from store
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export type AppStore = typeof store;
// Create pre-typed hooks
export const useAppSelector = useSelector.withTypes<RootState>();
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppStore = useStore.withTypes<AppStore>();Type definitions for React Redux component integration.
/**
* Props interface that includes dispatch function
*/
interface DispatchProp<A extends Action<string> = UnknownAction> {
dispatch: Dispatch<A>;
}
/**
* Component enhancer type for connect HOC
*/
type InferableComponentEnhancer<TInjectedProps> =
InferableComponentEnhancerWithProps<TInjectedProps, {}>;
type InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> =
<C extends ComponentType<Matching<TInjectedProps, GetProps<C>>>>(
component: C
) => ConnectedComponent<C, Mapped<DistributiveOmit<GetLibraryManagedProps<C>, keyof Shared<TInjectedProps, GetLibraryManagedProps<C>>> & TNeedsProps & ConnectPropsMaybeWithoutContext<TNeedsProps & GetProps<C>>>>;
/**
* Connected component type with original component reference
*/
type ConnectedComponent<C extends ComponentType<any>, P> =
FunctionComponent<P> & NonReactStatics<C> & { WrappedComponent: C };
/**
* Props type inference from connected component
*/
type ConnectedProps<TConnector> = TConnector extends InferableComponentEnhancerWithProps<infer TInjectedProps, any>
? unknown extends TInjectedProps
? TConnector extends InferableComponentEnhancer<infer TInjectedProps>
? TInjectedProps
: never
: TInjectedProps
: never;Comprehensive type definitions for connect HOC mapping functions.
/**
* Selector function types for connect
*/
type Selector<S, TProps, TOwnProps = null> = TOwnProps extends null | undefined
? (state: S) => TProps
: (state: S, ownProps: TOwnProps) => TProps;
type SelectorFactory<S, TProps, TOwnProps, TFactoryOptions> =
(dispatch: Dispatch<Action<string>>, factoryOptions: TFactoryOptions) => Selector<S, TProps, TOwnProps>;
/**
* State mapping function types
*/
type MapStateToProps<TStateProps, TOwnProps, State> =
(state: State, ownProps: TOwnProps) => TStateProps;
type MapStateToPropsFactory<TStateProps, TOwnProps, State> =
(initialState: State, ownProps: TOwnProps) => MapStateToProps<TStateProps, TOwnProps, State>;
type MapStateToPropsParam<TStateProps, TOwnProps, State> =
MapStateToPropsFactory<TStateProps, TOwnProps, State> |
MapStateToProps<TStateProps, TOwnProps, State> |
null |
undefined;
/**
* Dispatch mapping function types
*/
type MapDispatchToPropsFunction<TDispatchProps, TOwnProps> =
(dispatch: Dispatch<Action<string>>, ownProps: TOwnProps) => TDispatchProps;
type MapDispatchToProps<TDispatchProps, TOwnProps> =
MapDispatchToPropsFunction<TDispatchProps, TOwnProps> | TDispatchProps;
type MapDispatchToPropsFactory<TDispatchProps, TOwnProps> =
(dispatch: Dispatch<Action<string>>, ownProps: TOwnProps) => MapDispatchToPropsFunction<TDispatchProps, TOwnProps>;
type MapDispatchToPropsParam<TDispatchProps, TOwnProps> =
MapDispatchToPropsFactory<TDispatchProps, TOwnProps> |
MapDispatchToProps<TDispatchProps, TOwnProps>;
/**
* Thunk action creator type resolution
*/
type ResolveThunks<TDispatchProps> = TDispatchProps extends { [key: string]: any }
? { [C in keyof TDispatchProps]: HandleThunkActionCreator<TDispatchProps[C]> }
: TDispatchProps;
/**
* Props merging function type
*/
type MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps> =
(stateProps: TStateProps, dispatchProps: TDispatchProps, ownProps: TOwnProps) => TMergedProps;Common utility types used throughout React Redux type system.
/**
* Equality comparison function type
*/
type EqualityFn<T> = (a: T, b: T) => boolean;
/**
* Extended equality function with additional parameters
*/
type ExtendedEqualityFn<T, P> = (a: T, b: T, c: P, d: P) => boolean;
/**
* Utility type for empty objects
*/
type AnyIfEmpty<T extends object> = keyof T extends never ? any : T;
/**
* Distributive omit utility type
*/
type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;
/**
* Extract store action type from store instance
*/
type ExtractStoreActionType<StoreType extends Store> =
StoreType extends Store<any, infer ActionType> ? ActionType : never;
/**
* Props matching utility type for component enhancement
*/
type Matching<InjectedProps, DecorationTargetProps> = {
[P in keyof DecorationTargetProps]: P extends keyof InjectedProps
? InjectedProps[P] extends DecorationTargetProps[P]
? DecorationTargetProps[P]
: InjectedProps[P]
: DecorationTargetProps[P];
};
/**
* Shared props utility type
*/
type Shared<InjectedProps, DecorationTargetProps> = {
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]?: InjectedProps[P] extends DecorationTargetProps[P]
? DecorationTargetProps[P]
: never;
};
/**
* Get component props utility type
*/
type GetProps<C> = C extends ComponentType<infer P>
? C extends ComponentClass<P>
? ClassAttributes<InstanceType<C>> & P
: P
: never;
/**
* Get library managed props utility type
*/
type GetLibraryManagedProps<C> = JSX.LibraryManagedAttributes<C, GetProps<C>>;
/**
* Mapped utility type
*/
type Mapped<T> = Identity<{ [k in keyof T]: T[k] }>;
/**
* Identity utility type
*/
type Identity<T> = T;
/**
* Connect props handling utility
*/
type ConnectPropsMaybeWithoutContext<TActualOwnProps> = TActualOwnProps extends { context: any }
? Omit<ConnectProps, 'context'>
: ConnectProps;
/**
* Thunk action creator inference utilities
*/
type InferThunkActionCreatorType<TActionCreator extends (...args: any[]) => any> =
TActionCreator extends (...args: infer TParams) => (...args: any[]) => infer TReturn
? (...args: TParams) => TReturn
: TActionCreator;
type HandleThunkActionCreator<TActionCreator> = TActionCreator extends (...args: any[]) => any
? InferThunkActionCreatorType<TActionCreator>
: TActionCreator;
/**
* Non-object map dispatch to props type
*/
type MapDispatchToPropsNonObject<TDispatchProps, TOwnProps> =
MapDispatchToPropsFactory<TDispatchProps, TOwnProps> |
MapDispatchToPropsFunction<TDispatchProps, TOwnProps>;Handle multiple stores with different type configurations.
import { createContext } from "react";
import { createSelectorHook, createDispatchHook } from "react-redux";
// First store types
export type MainRootState = ReturnType<typeof mainStore.getState>;
export type MainAppDispatch = typeof mainStore.dispatch;
// Second store types
export type SecondaryRootState = ReturnType<typeof secondaryStore.getState>;
export type SecondaryAppDispatch = typeof secondaryStore.dispatch;
// Custom contexts
const SecondaryContext = createContext<ReactReduxContextValue<SecondaryRootState> | null>(null);
// Create typed hooks for secondary store
export const useSecondarySelector = createSelectorHook(SecondaryContext).withTypes<SecondaryRootState>();
export const useSecondaryDispatch = createDispatchHook(SecondaryContext).withTypes<SecondaryAppDispatch>();Advanced typing for complex connect scenarios.
interface StateProps {
user: User;
todos: Todo[];
}
interface DispatchProps {
fetchUser: (id: string) => void;
addTodo: (text: string) => void;
}
interface OwnProps {
userId: string;
}
type Props = StateProps & DispatchProps & OwnProps;
const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => ({
user: state.users[ownProps.userId],
todos: state.todos.filter(todo => todo.userId === ownProps.userId)
});
const mapDispatchToProps: MapDispatchToPropsParam<DispatchProps, OwnProps> = {
fetchUser: fetchUserAction,
addTodo: addTodoAction
};
const connector = connect(mapStateToProps, mapDispatchToProps);
type PropsFromRedux = ConnectedProps<typeof connector>;
const UserTodos: React.FC<PropsFromRedux & OwnProps> = ({ user, todos, fetchUser, addTodo, userId }) => {
// Fully typed component implementation
};
export default connector(UserTodos);Create reusable typed components with generics.
interface GenericListProps<T> {
items: T[];
onItemClick: (item: T) => void;
renderItem: (item: T) => React.ReactNode;
}
function GenericList<T>({ items, onItemClick, renderItem }: GenericListProps<T>) {
return (
<div>
{items.map((item, index) => (
<div key={index} onClick={() => onItemClick(item)}>
{renderItem(item)}
</div>
))}
</div>
);
}
// Usage with Redux
function TodoList() {
const todos = useAppSelector((state) => state.todos);
const dispatch = useAppDispatch();
return (
<GenericList<Todo>
items={todos}
onItemClick={(todo) => dispatch(selectTodo(todo.id))}
renderItem={(todo) => <span>{todo.text}</span>}
/>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-redux