Skip to content

Commit

Permalink
Merge pull request #4638 from exuanbo/patch-1
Browse files Browse the repository at this point in the history
fix(types/store): Unexpectedly narrowed return type of function `Store['getState']`
  • Loading branch information
EskiMojo14 committed Dec 23, 2023
2 parents 105e389 + 9e8a320 commit fa2d899
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 10 deletions.
15 changes: 8 additions & 7 deletions src/createStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
StoreEnhancer,
Dispatch,
Observer,
ListenerCallback
ListenerCallback,
UnknownIfNonSpecific
} from './types/store'
import { Action } from './types/actions'
import { Reducer } from './types/reducers'
Expand Down Expand Up @@ -46,7 +47,7 @@ export function createStore<
>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
/**
* @deprecated
*
Expand Down Expand Up @@ -82,7 +83,7 @@ export function createStore<
reducer: Reducer<S, A, PreloadedState>,
preloadedState?: PreloadedState | undefined,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
export function createStore<
S,
A extends Action,
Expand All @@ -93,7 +94,7 @@ export function createStore<
reducer: Reducer<S, A, PreloadedState>,
preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext {
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext {
if (typeof reducer !== 'function') {
throw new Error(
`Expected the root reducer to be a function. Instead, received: '${kindOf(
Expand Down Expand Up @@ -432,7 +433,7 @@ export function legacy_createStore<
>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
/**
* Creates a Redux store that holds the state tree.
*
Expand Down Expand Up @@ -473,7 +474,7 @@ export function legacy_createStore<
reducer: Reducer<S, A, PreloadedState>,
preloadedState?: PreloadedState | undefined,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
export function legacy_createStore<
S,
A extends Action,
Expand All @@ -484,6 +485,6 @@ export function legacy_createStore<
reducer: Reducer<S, A>,
preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext {
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext {
return createStore(reducer, preloadedState as any, enhancer)
}
8 changes: 5 additions & 3 deletions src/types/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export type Observer<T> = {
export interface Store<
S = any,
A extends Action = UnknownAction,
StateExt extends {} = {}
StateExt extends unknown = unknown
> {
/**
* Dispatches an action. It is the only way to trigger a state change.
Expand Down Expand Up @@ -164,6 +164,8 @@ export interface Store<
[Symbol.observable](): Observable<S & StateExt>
}

export type UnknownIfNonSpecific<T> = {} extends T ? unknown : T

/**
* A store creator is a function that creates a Redux store. Like with
* dispatching function, we must distinguish the base store creator,
Expand All @@ -180,7 +182,7 @@ export interface StoreCreator {
<S, A extends Action, Ext extends {} = {}, StateExt extends {} = {}>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
<
S,
A extends Action,
Expand All @@ -191,7 +193,7 @@ export interface StoreCreator {
reducer: Reducer<S, A, PreloadedState>,
preloadedState?: PreloadedState | undefined,
enhancer?: StoreEnhancer<Ext>
): Store<S, A, StateExt> & Ext
): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
}

/**
Expand Down
5 changes: 5 additions & 0 deletions test/typescript/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ const funcWithStore = (store: Store<State, DerivedAction>) => {}

const store: Store<State> = createStore(reducer)

// test that nullable state is preserved
const nullableStore = createStore((): string | null => null)

expectTypeOf(nullableStore.getState()).toEqualTypeOf<string | null>()

// ensure that an array-based state works
const arrayReducer = (state: any[] = []) => state || []
const storeWithArrayState: Store<any[]> = createStore(arrayReducer)
Expand Down

0 comments on commit fa2d899

Please sign in to comment.