Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(types/store): Unexpectedly narrowed return type of function Store['getState'] #4638

Merged
merged 4 commits into from
Dec 23, 2023

Conversation

exuanbo
Copy link
Contributor

@exuanbo exuanbo commented Dec 16, 2023


name: 🐛 Fix the return type of function Store['getState']
about: Unexpectedly narrowed type

PR Type

Does this PR add a new feature, or fix a bug?

This PR fixes a bug in the type definitions.

Why should this PR be included?

What is the current behavior, and the steps to reproduce the issue?

The return type of function Store['getState'] is unexpectedly narrowed to non-nullable.

import { type Reducer, createStore } from 'redux'

type NonNullableState = { count: number }
const nullableReducer: Reducer<NonNullableState | null> = () => null

const storeWithNullableState = createStore(nullableReducer)
const nullableState = storeWithNullableState.getState()
//    ^? const nullableState: NonNullableState

TS Playground: https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzjAnmApnASugJgVwGN0oAaOQqdAQxnQGUZpMBfOAMyghDgHIqCAD14AoEagxwAchAB2U-ABtF1AEaKGMWpgC8iChHyyYALjiz8IVSTgsRhOQGd4F5Wo04CxKGc9ESADwy8koq6pracAA+5qEAfHB6ABQAlIkJropiDrLOcM7MAOrAMAAWCm7hjJF6lDR0jMxJme7oft4p9k4uoa3VdIn5TFTFZRVhGv3oAHQA5ugwU6kiAPQrcBtwAHoA-GJAA

What is the expected behavior?

If StateExt type parameter is not provided, Store['getState'] should return the untouched type S.

How does this PR fix the problem?

The problem is in this line:

redux/src/types/store.ts

Lines 114 to 119 in 7876f8e

/**
* Reads the state tree managed by the store.
*
* @returns The current state tree of your application.
*/
getState(): S & StateExt

If StateExt is not given a specific value, the type will be S & {} which narrows the type to non-nullable.

The behavior of intersection with {} is changed in TypeScript 4.8, "Improved Intersection Reduction, Union Compatibility, and Narrowing".

In this PR:

  • The default value of the type parameter StateExt in the Store interface is widened to unknown. This can ensure that S & unknown is still S.
  • In other types for creating store, the type arguments StateExt passed to Store is widened using a utility type UnknownIfNonSpecific.
type UnknownIfNonSpecific<T> = {} extends T ? unknown : T

Checklist

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Is there an existing issue for this PR?
    • No.
  • Have the files been linted and formatted?
  • Have the docs been updated to match the changes in the PR?
  • Have the tests been updated to match the changes in the PR?
  • Have you run the tests locally to confirm they pass?

Copy link

codesandbox-ci bot commented Dec 16, 2023

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 9e8a320:

Sandbox Source
Vanilla Typescript Configuration

@EskiMojo14
Copy link
Contributor

good catch, thanks! admittedly i don't think stores with a nullable state are very common, but you're correct that this is an issue.

any chance you could check whether the same issue needs fixing on the RTK side of things too?

@EskiMojo14 EskiMojo14 merged commit fa2d899 into reduxjs:master Dec 23, 2023
23 checks passed
@EskiMojo14
Copy link
Contributor

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants