From 87cda7d4ff2ea1d62d9df72fc0e60d90f368863b Mon Sep 17 00:00:00 2001 From: Michael Dokolin Date: Thu, 25 Aug 2022 17:21:10 +0200 Subject: [PATCH 1/2] Fix the `dispatch` type inference to correctly handle read-only middleware arrays --- packages/toolkit/src/tsHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts index 640b8d6d5f..567d3f63e7 100644 --- a/packages/toolkit/src/tsHelpers.ts +++ b/packages/toolkit/src/tsHelpers.ts @@ -88,7 +88,7 @@ export type ExtractDispatchExtensions = M extends MiddlewareArray< infer MiddlewareTuple > ? ExtractDispatchFromMiddlewareTuple - : M extends Middleware[] + : M extends ReadonlyArray ? ExtractDispatchFromMiddlewareTuple<[...M], {}> : never From 6687a381406ac50904794f60197e4a11f51ba770 Mon Sep 17 00:00:00 2001 From: Michael Dokolin Date: Fri, 26 Aug 2022 13:08:57 +0200 Subject: [PATCH 2/2] Add typings tests to cover the correct handling of read-only middleware arrays --- .../src/tests/configureStore.typetest.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/toolkit/src/tests/configureStore.typetest.ts b/packages/toolkit/src/tests/configureStore.typetest.ts index 2f9183095d..69ad4be736 100644 --- a/packages/toolkit/src/tests/configureStore.typetest.ts +++ b/packages/toolkit/src/tests/configureStore.typetest.ts @@ -13,6 +13,7 @@ import { configureStore, getDefaultMiddleware, createSlice, + ConfigureStoreOptions, } from '@reduxjs/toolkit' import type { ThunkMiddleware, ThunkAction, ThunkDispatch } from 'redux-thunk' import thunk from 'redux-thunk' @@ -304,6 +305,18 @@ const _anyMiddleware: any = () => () => () => {} // @ts-expect-error const result2: string = store.dispatch(5) } + /** + * Test: read-only middleware tuple + */ + { + const store = configureStore({ + reducer: reducerA, + middleware: [] as any as readonly [Middleware<(a: StateA) => boolean, StateA>], + }) + const result: boolean = store.dispatch(5) + // @ts-expect-error + const result2: string = store.dispatch(5) + } /** * Test: multiple custom middleware */ @@ -473,6 +486,32 @@ const _anyMiddleware: any = () => () => () => {} expectNotAny(store.dispatch) } + /** + * Test: decorated `configureStore` won't make `dispatch` `never` + */ + { + const someSlice = createSlice({ + name: 'something', + initialState: null as any, + reducers: { + set(state) { + return state; + }, + }, + }); + + function configureMyStore(options: Omit, 'reducer'>) { + return configureStore({ + ...options, + reducer: someSlice.reducer, + }); + } + + const store = configureMyStore({}); + + expectType(store.dispatch); + } + { interface CounterState { value: number