This TypeScript error occurs when the useSelector hook from React Redux returns inconsistent types across different execution paths. It often happens when using equality functions like shallowEqual or when selector functions have conditional return types. The error prevents proper type inference and can lead to runtime issues.
Fixes useSelector must return the same type in every case
// ❌ Bad: Returns different types
const selectValue = (state: RootState) => {
if (state.someCondition) {
return state.data.value; // number
}
return null; // null
};
// ✅ Good: Always returns the same type
const selectValue = (state: RootState): number | null => {
if (state.someCondition) {
return state.data.value;
}
return null;
};// ❌ Bad: Returns different typesconst selectValue = (state: RootState) => {if (state.someCondition) {return state.data.value; // number}return null; // null};// ✅ Good: Always returns the same typeconst selectValue = (state: RootState): number | null => {if (state.someCondition) {return state.data.value;}return null;};useSelector must return the same type in every case
The "useSelector must return the same type in every case" error is a TypeScript compilation error that arises when TypeScript cannot guarantee that a useSelector hook will always return the same type of value. This typically happens when the selector function passed to useSelector has multiple return types based on conditions, or when using equality comparison functions that interfere with TypeScript's ability to infer consistent return types. React Redux relies on consistent typing to ensure type safety throughout your application.
Check that your selector function always returns the same type, regardless of conditions. Avoid returning undefined or different types in different branches.
// ❌ Bad: Returns different types
const selectValue = (state: RootState) => {
if (state.someCondition) {
return state.data.value; // number
}
return null; // null
};
// ✅ Good: Always returns the same type
const selectValue = (state: RootState): number | null => {
if (state.someCondition) {
return state.data.value;
}
return null;
};Create pre-typed versions of useSelector using React Redux's .withTypes() method or the traditional TypedUseSelectorHook approach.
// Using .withTypes() (React Redux v9.1.0+)
import { useSelector } from 'react-redux';
import type { RootState } from './store';
export const useAppSelector = useSelector.withTypes<RootState>();
// In your component
const value = useAppSelector(selectValue); // No second argument needed
// If you need equality comparison, consider alternativesIf you're using shallowEqual to prevent unnecessary re-renders, consider using memoized selectors with Reselect instead, which provides better TypeScript support.
import { createSelector } from '@reduxjs/toolkit';
const selectData = (state: RootState) => state.some.data;
const selectFilter = (state: RootState) => state.some.filter;
export const selectFilteredData = createSelector(
[selectData, selectFilter],
(data, filter) => data.filter(item => item.matches(filter))
);
// This selector is automatically memoized and has proper typing
const filteredData = useAppSelector(selectFilteredData);Ensure you have the latest type definitions installed. For React Redux v9+, types are included in the package, but you might need to update your @types/react-redux if you're on an older version.
npm update react-redux @types/react-reduxOr if using yarn:
yarn upgrade react-redux @types/react-reduxWhen your selector logic is complex, provide explicit return type annotations to help TypeScript understand the expected output.
interface ComplexResult {
items: Item[];
total: number;
hasMore: boolean;
}
const selectComplexData = (state: RootState): ComplexResult => {
// Complex logic here
return {
items: state.data.items,
total: state.data.total,
hasMore: state.data.hasMore
};
};
// TypeScript now knows the exact return type
const complexData = useSelector(selectComplexData);This issue became more prominent in React Redux v9.1.0+ due to changes in how TypeScript infers types with the .withTypes() method. The underlying problem is that TypeScript's type system has limitations when dealing with higher-order functions that accept equality comparators. For advanced use cases, consider using the useSelector hook without equality functions and implement performance optimizations at the selector level using Reselect's memoization capabilities. Additionally, ensure your Redux store state is properly typed using TypeScript's RootState pattern as documented in the official Redux Toolkit documentation.