diff --git a/src/createStore.ts b/src/createStore.ts index 64e6bdf2fa..e5f3b6d7ba 100644 --- a/src/createStore.ts +++ b/src/createStore.ts @@ -5,7 +5,8 @@ import { StoreEnhancer, Dispatch, Observer, - ListenerCallback + ListenerCallback, + UnknownIfNonSpecific } from './types/store' import { Action } from './types/actions' import { Reducer } from './types/reducers' @@ -46,7 +47,7 @@ export function createStore< >( reducer: Reducer, enhancer?: StoreEnhancer -): Store & Ext +): Store> & Ext /** * @deprecated * @@ -82,7 +83,7 @@ export function createStore< reducer: Reducer, preloadedState?: PreloadedState | undefined, enhancer?: StoreEnhancer -): Store & Ext +): Store> & Ext export function createStore< S, A extends Action, @@ -93,7 +94,7 @@ export function createStore< reducer: Reducer, preloadedState?: PreloadedState | StoreEnhancer | undefined, enhancer?: StoreEnhancer -): Store & Ext { +): Store> & Ext { if (typeof reducer !== 'function') { throw new Error( `Expected the root reducer to be a function. Instead, received: '${kindOf( @@ -432,7 +433,7 @@ export function legacy_createStore< >( reducer: Reducer, enhancer?: StoreEnhancer -): Store & Ext +): Store> & Ext /** * Creates a Redux store that holds the state tree. * @@ -473,7 +474,7 @@ export function legacy_createStore< reducer: Reducer, preloadedState?: PreloadedState | undefined, enhancer?: StoreEnhancer -): Store & Ext +): Store> & Ext export function legacy_createStore< S, A extends Action, @@ -484,6 +485,6 @@ export function legacy_createStore< reducer: Reducer, preloadedState?: PreloadedState | StoreEnhancer | undefined, enhancer?: StoreEnhancer -): Store & Ext { +): Store> & Ext { return createStore(reducer, preloadedState as any, enhancer) } diff --git a/src/types/store.ts b/src/types/store.ts index 0c6b4580c7..c3651d0a72 100644 --- a/src/types/store.ts +++ b/src/types/store.ts @@ -81,7 +81,7 @@ export type Observer = { 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. @@ -164,6 +164,8 @@ export interface Store< [Symbol.observable](): Observable } +export type UnknownIfNonSpecific = {} 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, @@ -180,7 +182,7 @@ export interface StoreCreator { ( reducer: Reducer, enhancer?: StoreEnhancer - ): Store & Ext + ): Store> & Ext < S, A extends Action, @@ -191,7 +193,7 @@ export interface StoreCreator { reducer: Reducer, preloadedState?: PreloadedState | undefined, enhancer?: StoreEnhancer - ): Store & Ext + ): Store> & Ext } /** diff --git a/test/typescript/store.ts b/test/typescript/store.ts index 55948e1329..834fb55686 100644 --- a/test/typescript/store.ts +++ b/test/typescript/store.ts @@ -61,6 +61,11 @@ const funcWithStore = (store: Store) => {} const store: Store = createStore(reducer) +// test that nullable state is preserved +const nullableStore = createStore((): string | null => null) + +expectTypeOf(nullableStore.getState()).toEqualTypeOf() + // ensure that an array-based state works const arrayReducer = (state: any[] = []) => state || [] const storeWithArrayState: Store = createStore(arrayReducer)