CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-redux

Official React bindings for Redux state management with hooks and higher-order components

Pending
Overview
Eval results
Files

typescript.mddocs/

TypeScript Integration

React Redux provides comprehensive TypeScript support with full type safety, pre-typed hooks, and extensive type definitions for all API surfaces.

Capabilities

Pre-typed Hooks (.withTypes() Methods)

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>;
}

Typed Hook Interface

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>();

Store Type Configuration

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>();

Component Type Definitions

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;

Selector and Dispatch Type Definitions

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;

Utility Types

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>;

Advanced TypeScript Usage

Multiple Store Types

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>();

Complex Connect Typing

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);

Generic Component Typing

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

docs

connect.md

hooks.md

index.md

typescript.md

tile.json