TypedUseSelectorHook infers `any` with `shallowEqual` since 9.3.1 (NoInfer dropped)

Open Beginner friendly
#2,416 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript
Domain
testing-qa

Research direction

Start with src/types.ts, where TypedUseSelectorHook defines the equalityFn parameter, and compare its NoInfer usage with the UseSelector overloads. Add a type test for shallowEqual with strictFunctionTypes disabled, as described in the issue. Done when the test confirms the selector return type is preserved and the regression is covered.

Written by the indexing model from the issue text.

Description

Heads-up: this was investigated and drafted with the help of a coding agent, sorry about that in advance. We checked the repro and cause by hand against the published 9.3.0 / 9.3.1 packages, and it looks like a real regression, so we wanted to report it.

What version of React, ReactDOM/React Native, Redux, and React Redux are you using?
  • React: 19.2.6
  • ReactDOM: 19.2.6
  • Redux: 5.0.1
  • React Redux: 9.3.1 (also 9.4.0-alpha.2). TypeScript 5.9.3
What is the current behavior?

With strictFunctionTypes off (for example "strict": false), a TypedUseSelectorHook called with shallowEqual now infers its result as any. In 9.3.0 it inferred the selector's return type.

import { shallowEqual, type TypedUseSelectorHook, useSelector } from 'react-redux'

type RootState = { plans: Record<string, { key: string }> }
const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

const result = useAppSelector(s => ({ plans: s.plans }), shallowEqual)
const probe: number = result
// 9.3.0: error, result is { plans: Record<string, { key: string }> }
// 9.3.1: no error, result is any

tsc --noEmit --strict --strictFunctionTypes false repro.ts

In our app this silently turned ~200 selectors into any. We only noticed because a few Object.values(...) calls became unknown[].

Cause: 520b2001f4 (removing the custom NoInfer helper, #2313) dropped the wrapper from TypedUseSelectorHook instead of switching to the native NoInfer:

// src/types.ts
-    equalityFn?: EqualityFn<NoInfer<TSelected>>,
+    equalityFn?: EqualityFn<TSelected>,

With bivariant parameter checking, shallowEqual(objA: any, objB: any) becomes an inference candidate for TSelected, and any wins. This is the same symptom as #2186 / #1964.

What is the expected behavior?

TypedUseSelectorHook keeps inferring the selector's return type, by using the native NoInfer: equalityFn?: EqualityFn<NoInfer<TSelected>>. A type test for shallowEqual with strictFunctionTypes: false would catch it next time.

Side note: useSelector.withTypes<RootState>() and a plain useSelector((s: RootState) => ..., shallowEqual) infer any under the same setting on 9.3.0 too, because the UseSelector interface overloads don't use NoInfer. So that part isn't a regression, but the same fix would cover it.

Which browser and OS are affected by this issue?

None, it's types only.

Did this work in previous versions of React Redux?
  • Yes (9.3.0)
Dominant language
TypeScript
Stars
23.4k
Forks
3.3k
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from reduxjs/react-redux

All issues in reduxjs/react-redux

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.