diff --git a/docs/api/configureStore.mdx b/docs/api/configureStore.mdx index 4d5f9afab3..a722a03cb2 100644 --- a/docs/api/configureStore.mdx +++ b/docs/api/configureStore.mdx @@ -20,7 +20,7 @@ to the store setup for a better development experience. interface ConfigureStoreOptions< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, M extends Middlewares = Middlewares E extends Enhancers = Enhancers > { @@ -64,7 +64,7 @@ interface ConfigureStoreOptions< enhancers?: (getDefaultEnhancers: GetDefaultEnhancers) => E | E } -function configureStore( +function configureStore( options: ConfigureStoreOptions ): EnhancedStore ``` diff --git a/docs/api/createDynamicMiddleware.mdx b/docs/api/createDynamicMiddleware.mdx index 5c86b80077..6aea1fa3a6 100644 --- a/docs/api/createDynamicMiddleware.mdx +++ b/docs/api/createDynamicMiddleware.mdx @@ -80,7 +80,7 @@ The "dynamic middleware instance" returned from `createDynamicMiddleware` is an ```ts no-transpile export type DynamicMiddlewareInstance< State = unknown, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = { middleware: DynamicMiddleware addMiddleware: AddMiddleware @@ -131,7 +131,7 @@ _These depend on having `react-redux` installed._ ```ts no-transpile interface ReactDynamicMiddlewareInstance< State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > extends DynamicMiddlewareInstance { createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook< State, diff --git a/docs/api/createListenerMiddleware.mdx b/docs/api/createListenerMiddleware.mdx index 5913f28055..7d509e7269 100644 --- a/docs/api/createListenerMiddleware.mdx +++ b/docs/api/createListenerMiddleware.mdx @@ -120,10 +120,10 @@ The "listener middleware instance" returned from `createListenerMiddleware` is a ```ts no-transpile interface ListenerMiddlewareInstance< State = unknown, - Dispatch extends ThunkDispatch = ThunkDispatch< + Dispatch extends ThunkDispatch = ThunkDispatch< State, unknown, - AnyAction + UnknownAction >, ExtraArgument = unknown > { @@ -181,7 +181,7 @@ interface AddListenerOptions { effect: (action: Action, listenerApi: ListenerApi) => void | Promise } -type ListenerPredicate = ( +type ListenerPredicate = ( action: Action, currentState?: State, originalState?: State @@ -321,7 +321,7 @@ The `listenerApi` object is the second argument to each listener callback. It co ```ts no-transpile export interface ListenerEffectAPI< State, - Dispatch extends ReduxDispatch, + Dispatch extends ReduxDispatch, ExtraArgument = unknown > extends MiddlewareAPI { // NOTE: MiddlewareAPI contains `dispatch` and `getState` already @@ -572,12 +572,12 @@ Listeners can use the `condition` and `take` methods in `listenerApi` to wait un The signatures are: ```ts no-transpile -type ConditionFunction = ( +type ConditionFunction = ( predicate: ListenerPredicate | (() => boolean), timeout?: number ) => Promise -type TakeFunction = ( +type TakeFunction = ( predicate: ListenerPredicate | (() => boolean), timeout?: number ) => Promise<[Action, State, State] | null> diff --git a/docs/api/matching-utilities.mdx b/docs/api/matching-utilities.mdx index 16c720dc2c..2aec61a137 100644 --- a/docs/api/matching-utilities.mdx +++ b/docs/api/matching-utilities.mdx @@ -49,12 +49,12 @@ A higher-order function that returns a type guard function that may be used to c ```ts title="isAsyncThunkAction usage" import { isAsyncThunkAction } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers' const isARequestAction = isAsyncThunkAction(requestThunk1, requestThunk2) -function handleRequestAction(action: AnyAction) { +function handleRequestAction(action: UnknownAction) { if (isARequestAction(action)) { // action is an action dispatched by either `requestThunk1` or `requestThunk2` } @@ -67,12 +67,12 @@ A higher-order function that returns a type guard function that may be used to c ```ts title="isPending usage" import { isPending } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers' const isAPendingAction = isPending(requestThunk1, requestThunk2) -function handlePendingAction(action: AnyAction) { +function handlePendingAction(action: UnknownAction) { if (isAPendingAction(action)) { // action is a pending action dispatched by either `requestThunk1` or `requestThunk2` } @@ -85,12 +85,12 @@ A higher-order function that returns a type guard function that may be used to c ```ts title="isFulfilled usage" import { isFulfilled } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers' const isAFulfilledAction = isFulfilled(requestThunk1, requestThunk2) -function handleFulfilledAction(action: AnyAction) { +function handleFulfilledAction(action: UnknownAction) { if (isAFulfilledAction(action)) { // action is a fulfilled action dispatched by either `requestThunk1` or `requestThunk2` } @@ -103,12 +103,12 @@ A higher-order function that returns a type guard function that may be used to c ```ts title="isRejected usage" import { isRejected } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers' const isARejectedAction = isRejected(requestThunk1, requestThunk2) -function handleRejectedAction(action: AnyAction) { +function handleRejectedAction(action: UnknownAction) { if (isARejectedAction(action)) { // action is a rejected action dispatched by either `requestThunk1` or `requestThunk2` } @@ -121,7 +121,7 @@ A higher-order function that returns a type guard function that may be used to c ```ts title="isRejectedWithValue usage" import { isRejectedWithValue } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { requestThunk1, requestThunk2 } from '@virtual/matchers' const isARejectedWithValueAction = isRejectedWithValue( @@ -129,7 +129,7 @@ const isARejectedWithValueAction = isRejectedWithValue( requestThunk2 ) -function handleRejectedWithValueAction(action: AnyAction) { +function handleRejectedWithValueAction(action: UnknownAction) { if (isARejectedWithValueAction(action)) { // action is a rejected action dispatched by either `requestThunk1` or `requestThunk2` // where rejectWithValue was used @@ -145,10 +145,7 @@ we're able to easily use the same matcher for several cases in a type-safe manne First, let's examine an unnecessarily complex example: ```ts title="Example without using a matcher utility" -import { - createAsyncThunk, - createReducer, -} from '@reduxjs/toolkit' +import { createAsyncThunk, createReducer } from '@reduxjs/toolkit' import type { PayloadAction } from '@reduxjs/toolkit' interface Data { diff --git a/docs/rtk-query/api/createApi.mdx b/docs/rtk-query/api/createApi.mdx index d4984f9362..dc8d6e76bc 100644 --- a/docs/rtk-query/api/createApi.mdx +++ b/docs/rtk-query/api/createApi.mdx @@ -57,7 +57,7 @@ export const { useGetPokemonByNameQuery } = pokemonApi baseQuery(args: InternalQueryArgs, api: BaseQueryApi, extraOptions?: DefinitionExtraOptions): any; endpoints(build: EndpointBuilder): Definitions; extractRehydrationInfo?: ( - action: AnyAction, + action: UnknownAction, { reducerPath, }: { @@ -88,7 +88,7 @@ export const { useGetPokemonByNameQuery } = pokemonApi - `dispatch` - The `store.dispatch` method for the corresponding Redux store - `getState` - A function that may be called to access the current store state - `extra` - Provided as thunk.extraArgument to the configureStore getDefaultMiddleware option. - - `endpoint` - The name of the endpoint. + - `endpoint` - The name of the endpoint. - `type` - Type of request (`query` or `mutation`). - `forced` - Indicates if a query has been forced. - `extraOptions` - The value of the optional `extraOptions` property provided for a given endpoint diff --git a/docs/rtk-query/api/created-api/api-slice-utils.mdx b/docs/rtk-query/api/created-api/api-slice-utils.mdx index 6471d812e9..7698b0c7f9 100644 --- a/docs/rtk-query/api/created-api/api-slice-utils.mdx +++ b/docs/rtk-query/api/created-api/api-slice-utils.mdx @@ -30,7 +30,7 @@ const updateQueryData = ( endpointName: string, args: any, updateRecipe: (draft: Draft) => void -) => ThunkAction; +) => ThunkAction; interface PatchCollection { patches: Patch[]; @@ -119,7 +119,7 @@ const upsertQueryData = ( endpointName: string, args: any, newEntryData: T -) => ThunkAction>, PartialState, any, AnyAction>; +) => ThunkAction>, PartialState, any, UnknownAction>; ``` - **Parameters** @@ -156,7 +156,7 @@ const patchQueryData = ( endpointName: string, args: any patches: Patch[] -) => ThunkAction; +) => ThunkAction; ``` - **Parameters** @@ -203,7 +203,7 @@ const prefetch = ( endpointName: string, arg: any, options: PrefetchOptions -) => ThunkAction; +) => ThunkAction; ``` - **Parameters** @@ -229,42 +229,44 @@ dispatch(api.util.prefetch('getPosts', undefined, { force: true })) ``` ### `selectInvalidatedBy` - + #### Signature - + ```ts no-transpile - function selectInvalidatedBy( - state: RootState, - tags: ReadonlyArray> - ): Array<{ - endpointName: string - originalArgs: any - queryCacheKey: QueryCacheKey - }> +function selectInvalidatedBy( + state: RootState, + tags: ReadonlyArray> +): Array<{ + endpointName: string + originalArgs: any + queryCacheKey: QueryCacheKey +}> ``` - + - **Parameters** - `state`: the root state - `tags`: a readonly array of invalidated tags, where the provided `TagDescription` is one of the strings provided to the [`tagTypes`](../createApi.mdx#tagtypes) property of the api. e.g. - `[TagType]` - `[{ type: TagType }]` - `[{ type: TagType, id: number | string }]` - + #### Description - + A function that can select query parameters to be invalidated. - + The function accepts two arguments - - the root state and - - the cache tags to be invalidated. - + +- the root state and +- the cache tags to be invalidated. + It returns an array that contains - - the endpoint name, - - the original args and - - the queryCacheKey. - + +- the endpoint name, +- the original args and +- the queryCacheKey. + #### Example - + ```ts no-transpile dispatch(api.util.selectInvalidatedBy(state, ['Post'])) dispatch(api.util.selectInvalidatedBy(state, [{ type: 'Post', id: 1 }])) diff --git a/docs/rtk-query/api/created-api/endpoints.mdx b/docs/rtk-query/api/created-api/endpoints.mdx index 5d49dd6d71..ed4178467d 100644 --- a/docs/rtk-query/api/created-api/endpoints.mdx +++ b/docs/rtk-query/api/created-api/endpoints.mdx @@ -33,12 +33,12 @@ type InitiateRequestThunk = StartQueryActionCreator | StartMutationActionCreator type StartQueryActionCreator = ( arg:any, options?: StartQueryActionCreatorOptions -) => ThunkAction; +) => ThunkAction; type StartMutationActionCreator> = ( arg: any options?: StartMutationActionCreatorOptions -) => ThunkAction, any, any, AnyAction>; +) => ThunkAction, any, any, UnknownAction>; type SubscriptionOptions = { /** diff --git a/docs/usage/usage-with-typescript.md b/docs/usage/usage-with-typescript.md index 9fb7f9ab43..9f28586a47 100644 --- a/docs/usage/usage-with-typescript.md +++ b/docs/usage/usage-with-typescript.md @@ -252,7 +252,7 @@ As the first `matcher` argument to `builder.addMatcher`, a [type predicate](http As a result, the `action` argument for the second `reducer` argument can be inferred by TypeScript: ```ts -function isNumberValueAction(action: AnyAction): action is PayloadAction<{ value: number }> { +function isNumberValueAction(action: UnknownAction): action is PayloadAction<{ value: number }> { return typeof action.payload.value === 'number' } diff --git a/examples/action-listener/counter/src/services/counter/listeners.ts b/examples/action-listener/counter/src/services/counter/listeners.ts index 3fe74b7bfd..ac2d0c4a3d 100644 --- a/examples/action-listener/counter/src/services/counter/listeners.ts +++ b/examples/action-listener/counter/src/services/counter/listeners.ts @@ -1,6 +1,6 @@ import { counterActions, counterSelectors } from './slice' import { - AnyAction, + UnknownAction, isAllOf, isAnyOf, PayloadAction, @@ -11,7 +11,7 @@ import type { AppListenerEffectAPI, AppStartListening } from '../../store' function shouldStopAsyncTasksOf(id: string) { return isAllOf( isAnyOf(counterActions.cancelAsyncUpdates, counterActions.removeCounter), - (action: AnyAction): action is PayloadAction => + (action: UnknownAction): action is PayloadAction => action?.payload === id ) } diff --git a/packages/toolkit/etc/redux-toolkit.api.md b/packages/toolkit/etc/redux-toolkit.api.md index 5a113e6378..8628682b33 100644 --- a/packages/toolkit/etc/redux-toolkit.api.md +++ b/packages/toolkit/etc/redux-toolkit.api.md @@ -5,7 +5,7 @@ ```ts import type { Action } from 'redux' import type { ActionCreator } from 'redux' -import type { AnyAction } from 'redux' +import type { UnknownAction } from 'redux' import type { CombinedState } from 'redux' import { default as createNextState } from 'immer' import { createSelector } from 'reselect' @@ -85,10 +85,10 @@ export interface ActionReducerMapBuilder { type: Type, reducer: CaseReducer ): ActionReducerMapBuilder - addDefaultCase(reducer: CaseReducer): {} + addDefaultCase(reducer: CaseReducer): {} addMatcher( matcher: TypeGuard | ((action: any) => boolean), - reducer: CaseReducer + reducer: CaseReducer ): Omit, 'addCase'> } @@ -191,7 +191,7 @@ export type AsyncThunkPayloadCreatorReturnValue< > // @public -export type CaseReducer = ( +export type CaseReducer = ( state: Draft, action: A ) => S | void | Draft @@ -227,14 +227,14 @@ export type ConfigureEnhancersCallback = ( // @public export function configureStore< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, M extends Middlewares = [ThunkMiddlewareFor] >(options: ConfigureStoreOptions): EnhancedStore // @public export interface ConfigureStoreOptions< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, M extends Middlewares = Middlewares > { devTools?: boolean | EnhancerOptions @@ -352,7 +352,7 @@ export { Draft } // @public export interface EnhancedStore< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, M extends Middlewares = Middlewares > extends Store { dispatch: Dispatch & DispatchForMiddlewares diff --git a/packages/toolkit/etc/rtk-query.api.md b/packages/toolkit/etc/rtk-query.api.md index aeeb76f660..dea69f7421 100644 --- a/packages/toolkit/etc/rtk-query.api.md +++ b/packages/toolkit/etc/rtk-query.api.md @@ -4,7 +4,7 @@ ```ts import type { ActionCreatorWithoutPayload } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import type { SerializedError } from '@reduxjs/toolkit' import type { ThunkDispatch } from '@reduxjs/toolkit' @@ -127,7 +127,7 @@ export interface CreateApiOptions< build: EndpointBuilder ): Definitions extractRehydrationInfo?: ( - action: AnyAction, + action: UnknownAction, { reducerPath, }: { diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 80c07e1753..1646b3fbe1 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -111,7 +111,7 @@ ], "dependencies": { "immer": "^10.0.2", - "redux": "5.0.0-alpha.6", + "redux": "5.0.0-beta.0", "redux-thunk": "3.0.0-alpha.3", "reselect": "^5.0.0-alpha.2" }, diff --git a/packages/toolkit/src/combineSlices.ts b/packages/toolkit/src/combineSlices.ts index 2ef5ad7a1e..72c7750c7f 100644 --- a/packages/toolkit/src/combineSlices.ts +++ b/packages/toolkit/src/combineSlices.ts @@ -1,4 +1,4 @@ -import type { AnyAction, Reducer, StateFromReducersMapObject } from 'redux' +import type { UnknownAction, Reducer, StateFromReducersMapObject } from 'redux' import { combineReducers } from 'redux' import { nanoid } from './nanoid' import type { @@ -56,7 +56,7 @@ export type InjectConfig = { export interface CombinedSliceReducer< InitialState, DeclaredState = InitialState -> extends Reducer> { +> extends Reducer> { /** * Provide a type for slices that will be injected lazily. * @@ -378,7 +378,10 @@ export function combineSlices>( let reducer = getReducer() - function combinedReducer(state: Record, action: AnyAction) { + function combinedReducer( + state: Record, + action: UnknownAction + ) { return reducer(state, action) } diff --git a/packages/toolkit/src/configureStore.ts b/packages/toolkit/src/configureStore.ts index 44a06894f2..868187add1 100644 --- a/packages/toolkit/src/configureStore.ts +++ b/packages/toolkit/src/configureStore.ts @@ -3,10 +3,9 @@ import type { ReducersMapObject, Middleware, Action, - AnyAction, StoreEnhancer, Store, - Dispatch, + UnknownAction, } from 'redux' import { applyMiddleware, createStore, compose, combineReducers } from 'redux' import type { DevToolsEnhancerOptions as DevToolsOptions } from './devtoolsExtension' @@ -36,7 +35,7 @@ const IS_PRODUCTION = process.env.NODE_ENV === 'production' */ export interface ConfigureStoreOptions< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, M extends Tuple> = Tuple>, E extends Tuple = Tuple, P = S @@ -97,7 +96,7 @@ type Enhancers = ReadonlyArray */ export type EnhancedStore< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, E extends Enhancers = Enhancers > = ExtractStoreExtensions & Store, A> @@ -111,7 +110,7 @@ export type EnhancedStore< */ export function configureStore< S = any, - A extends Action = AnyAction, + A extends Action = UnknownAction, M extends Tuple> = Tuple<[ThunkMiddlewareFor]>, E extends Tuple = Tuple< [StoreEnhancer<{ dispatch: ExtractDispatchExtensions }>, StoreEnhancer] @@ -200,5 +199,5 @@ export function configureStore< const composedEnhancer: StoreEnhancer = finalCompose(...storeEnhancers) - return createStore(rootReducer, preloadedState, composedEnhancer) + return createStore(rootReducer, preloadedState as P, composedEnhancer) } diff --git a/packages/toolkit/src/createAction.ts b/packages/toolkit/src/createAction.ts index eb3f8abac2..dbfbeb70a9 100644 --- a/packages/toolkit/src/createAction.ts +++ b/packages/toolkit/src/createAction.ts @@ -1,4 +1,4 @@ -import type { Action } from 'redux' +import type { Action, UnknownAction } from 'redux' import type { IsUnknownOrNonInferrable, IfMaybeUndefined, diff --git a/packages/toolkit/src/createAsyncThunk.ts b/packages/toolkit/src/createAsyncThunk.ts index bbdd27c535..eca550d201 100644 --- a/packages/toolkit/src/createAsyncThunk.ts +++ b/packages/toolkit/src/createAsyncThunk.ts @@ -1,4 +1,4 @@ -import type { Dispatch, AnyAction } from 'redux' +import type { Dispatch, UnknownAction } from 'redux' import type { PayloadAction, ActionCreatorWithPreparedPayload, @@ -132,10 +132,14 @@ type GetDispatch = ThunkApiConfig extends { ThunkDispatch< GetState, GetExtra, - AnyAction + UnknownAction > > - : ThunkDispatch, GetExtra, AnyAction> + : ThunkDispatch< + GetState, + GetExtra, + UnknownAction + > export type GetThunkAPI = BaseThunkAPI< GetState, @@ -629,7 +633,7 @@ If you want to use the AbortController to react to \`abort\` events, please cons { requestId, arg }, { getState, extra } ) - ) + ) as any ) finalAction = await Promise.race([ abortedPromise, @@ -679,7 +683,7 @@ If you want to use the AbortController to react to \`abort\` events, please cons (finalAction as any).meta.condition if (!skipDispatch) { - dispatch(finalAction) + dispatch(finalAction as any) } return finalAction })() diff --git a/packages/toolkit/src/createReducer.ts b/packages/toolkit/src/createReducer.ts index e22b0a007a..e81f2b1e06 100644 --- a/packages/toolkit/src/createReducer.ts +++ b/packages/toolkit/src/createReducer.ts @@ -1,9 +1,9 @@ import type { Draft } from 'immer' import { produce as createNextState, isDraft, isDraftable } from 'immer' -import type { AnyAction, Action, Reducer } from 'redux' +import type { Action, Reducer, UnknownAction } from 'redux' import type { ActionReducerMapBuilder } from './mapBuilders' import { executeReducerBuilderCallback } from './mapBuilders' -import type { NoInfer } from './tsHelpers' +import type { NoInfer, TypeGuard } from './tsHelpers' import { freezeDraftable } from './utils' /** @@ -16,15 +16,8 @@ import { freezeDraftable } from './utils' */ export type Actions = Record -/** - * @deprecated use `TypeGuard` instead - */ -export interface ActionMatcher { - (action: AnyAction): action is A -} - -export type ActionMatcherDescription = { - matcher: ActionMatcher +export type ActionMatcherDescription = { + matcher: TypeGuard reducer: CaseReducer> } @@ -52,7 +45,7 @@ export type ActionMatcherDescriptionCollection = Array< * * @public */ -export type CaseReducer = ( +export type CaseReducer = ( state: Draft, action: A ) => NoInfer | void | Draft> @@ -108,7 +101,7 @@ let hasWarnedAboutObjectNotation = false import { createAction, createReducer, - AnyAction, + UnknownAction, PayloadAction, } from "@reduxjs/toolkit"; @@ -116,7 +109,7 @@ const increment = createAction("increment"); const decrement = createAction("decrement"); function isActionWithNumberPayload( - action: AnyAction + action: UnknownAction ): action is PayloadAction { return typeof action.payload === "number"; } diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts index cb69c3b0ed..85378dde78 100644 --- a/packages/toolkit/src/createSlice.ts +++ b/packages/toolkit/src/createSlice.ts @@ -1,4 +1,4 @@ -import type { Action, AnyAction, Reducer } from 'redux' +import type { Action, UnknownAction, Reducer } from 'redux' import type { ActionCreatorWithoutPayload, PayloadAction, @@ -186,7 +186,7 @@ export interface CreateSliceOptions< * * @example ```ts -import { createAction, createSlice, Action, AnyAction } from '@reduxjs/toolkit' +import { createAction, createSlice, Action, UnknownAction } from '@reduxjs/toolkit' const incrementBy = createAction('incrementBy') const decrement = createAction('decrement') @@ -194,7 +194,7 @@ interface RejectedAction extends Action { error: Error } -function isRejectedAction(action: AnyAction): action is RejectedAction { +function isRejectedAction(action: UnknownAction): action is RejectedAction { return action.type.endsWith('rejected') } @@ -240,7 +240,7 @@ interface ReducerDefinition { [reducerDefinitionType]: T } -export interface CaseReducerDefinition +export interface CaseReducerDefinition extends CaseReducer, ReducerDefinition {} diff --git a/packages/toolkit/src/dynamicMiddleware/index.ts b/packages/toolkit/src/dynamicMiddleware/index.ts index b1c7dc87f6..3b0583953f 100644 --- a/packages/toolkit/src/dynamicMiddleware/index.ts +++ b/packages/toolkit/src/dynamicMiddleware/index.ts @@ -1,7 +1,7 @@ import type { Middleware, Dispatch as ReduxDispatch, - AnyAction, + UnknownAction, MiddlewareAPI, } from 'redux' import { compose } from 'redux' @@ -18,7 +18,7 @@ import type { const createMiddlewareEntry = < State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch >( middleware: Middleware ): MiddlewareEntry => ({ @@ -29,7 +29,7 @@ const createMiddlewareEntry = < export const createDynamicMiddleware = < State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch >(): DynamicMiddlewareInstance => { const instanceId = nanoid() const middlewareMap = new Map>() diff --git a/packages/toolkit/src/dynamicMiddleware/react/index.ts b/packages/toolkit/src/dynamicMiddleware/react/index.ts index 858cdde7d7..7315f93394 100644 --- a/packages/toolkit/src/dynamicMiddleware/react/index.ts +++ b/packages/toolkit/src/dynamicMiddleware/react/index.ts @@ -1,6 +1,6 @@ import type { Action as ReduxAction, - AnyAction, + UnknownAction, Dispatch as ReduxDispatch, Middleware, } from 'redux' @@ -23,12 +23,12 @@ import type { export type UseDispatchWithMiddlewareHook< Middlewares extends Middleware[] = [], State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = () => ExtractDispatchExtensions & Dispatch export type CreateDispatchWithMiddlewareHook< State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = { < Middlewares extends [ @@ -51,7 +51,7 @@ type ActionFromDispatch> = interface ReactDynamicMiddlewareInstance< State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > extends DynamicMiddlewareInstance { createDispatchWithMiddlewareHookFactory: ( context?: Context< @@ -66,7 +66,7 @@ interface ReactDynamicMiddlewareInstance< export const createDynamicMiddleware = < State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch >(): ReactDynamicMiddlewareInstance => { const instance = cDM() const createDispatchWithMiddlewareHookFactory = ( diff --git a/packages/toolkit/src/dynamicMiddleware/tests/index.typetest.ts b/packages/toolkit/src/dynamicMiddleware/tests/index.typetest.ts index 1d4a1e14fa..386b1aaa9d 100644 --- a/packages/toolkit/src/dynamicMiddleware/tests/index.typetest.ts +++ b/packages/toolkit/src/dynamicMiddleware/tests/index.typetest.ts @@ -1,5 +1,5 @@ /* eslint-disable no-lone-blocks */ -import type { Action, AnyAction, Middleware } from 'redux' +import type { Action, UnknownAction, Middleware } from 'redux' import type { ThunkDispatch } from 'redux-thunk' import { createDynamicMiddleware } from '../index' import { configureStore } from '../../configureStore' @@ -7,7 +7,7 @@ import { expectExactType, expectType } from '../../tests/helpers' const untypedInstance = createDynamicMiddleware() -interface AppDispatch extends ThunkDispatch { +interface AppDispatch extends ThunkDispatch { (n: 1): 1 } diff --git a/packages/toolkit/src/dynamicMiddleware/tests/react.typetest.ts b/packages/toolkit/src/dynamicMiddleware/tests/react.typetest.ts index db664b4d54..59088fd3b5 100644 --- a/packages/toolkit/src/dynamicMiddleware/tests/react.typetest.ts +++ b/packages/toolkit/src/dynamicMiddleware/tests/react.typetest.ts @@ -1,13 +1,13 @@ /* eslint-disable no-lone-blocks */ import type { Context } from 'react' import type { ReactReduxContextValue } from 'react-redux' -import type { Action, AnyAction, Middleware } from 'redux' +import type { Action, UnknownAction, Middleware } from 'redux' import type { ThunkDispatch } from 'redux-thunk' import { createDynamicMiddleware } from '../react' import { expectExactType, expectType } from '../../tests/helpers' /* eslint-disable no-lone-blocks */ -interface AppDispatch extends ThunkDispatch { +interface AppDispatch extends ThunkDispatch { (n: 1): 1 } diff --git a/packages/toolkit/src/dynamicMiddleware/types.ts b/packages/toolkit/src/dynamicMiddleware/types.ts index c96689141d..234b1578e1 100644 --- a/packages/toolkit/src/dynamicMiddleware/types.ts +++ b/packages/toolkit/src/dynamicMiddleware/types.ts @@ -1,7 +1,7 @@ import type { Middleware, Dispatch as ReduxDispatch, - AnyAction, + UnknownAction, MiddlewareAPI, } from 'redux' import type { ExtractDispatchExtensions, FallbackIfUnknown } from '../tsHelpers' @@ -32,7 +32,7 @@ export type GetDispatch = MiddlewareApiConfig extends { export type AddMiddleware< State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = { (...middlewares: Middleware[]): void withTypes(): AddMiddleware< @@ -43,7 +43,7 @@ export type AddMiddleware< export interface WithMiddleware< State = any, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > extends BaseActionCreator< Middleware[], 'dynamicMiddleware/add', @@ -67,7 +67,7 @@ export interface DynamicDispatch { export type MiddlewareEntry< State = unknown, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = { id: string middleware: Middleware @@ -79,12 +79,12 @@ export type MiddlewareEntry< export type DynamicMiddleware< State = unknown, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = Middleware export type DynamicMiddlewareInstance< State = unknown, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = { middleware: DynamicMiddleware addMiddleware: AddMiddleware diff --git a/packages/toolkit/src/getDefaultMiddleware.ts b/packages/toolkit/src/getDefaultMiddleware.ts index acfd94d840..6ec507b4ce 100644 --- a/packages/toolkit/src/getDefaultMiddleware.ts +++ b/packages/toolkit/src/getDefaultMiddleware.ts @@ -1,4 +1,4 @@ -import type { Middleware, AnyAction } from 'redux' +import type { Middleware, UnknownAction } from 'redux' import type { ThunkMiddleware } from 'redux-thunk' import { thunk as thunkMiddleware, withExtraArgument } from 'redux-thunk' import type { ActionCreatorInvariantMiddlewareOptions } from './actionCreatorInvariantMiddleware' @@ -36,8 +36,8 @@ export type ThunkMiddlewareFor< } ? never : O extends { thunk: { extraArgument: infer E } } - ? ThunkMiddleware - : ThunkMiddleware + ? ThunkMiddleware + : ThunkMiddleware export type GetDefaultMiddleware = < O extends GetDefaultMiddlewareOptions = { diff --git a/packages/toolkit/src/listenerMiddleware/index.ts b/packages/toolkit/src/listenerMiddleware/index.ts index 8d989dade0..59194aa083 100644 --- a/packages/toolkit/src/listenerMiddleware/index.ts +++ b/packages/toolkit/src/listenerMiddleware/index.ts @@ -1,4 +1,4 @@ -import type { Dispatch, AnyAction, MiddlewareAPI } from 'redux' +import type { Action, Dispatch, MiddlewareAPI, UnknownAction } from 'redux' import type { ThunkDispatch } from 'redux-thunk' import { createAction, isAction } from '../createAction' import { nanoid } from '../nanoid' @@ -127,11 +127,7 @@ const createFork = ( } const createTakePattern = ( - startListening: AddListenerOverloads< - UnsubscribeListener, - S, - Dispatch - >, + startListening: AddListenerOverloads, signal: AbortSignal ): TakePattern => { /** @@ -150,7 +146,7 @@ const createTakePattern = ( // Placeholder unsubscribe function until the listener is added let unsubscribe: UnsubscribeListener = () => {} - const tuplePromise = new Promise<[AnyAction, S, S]>((resolve, reject) => { + const tuplePromise = new Promise<[Action, S, S]>((resolve, reject) => { // Inside the Promise, we synchronously add the listener. let stopListening = startListening({ predicate: predicate as any, @@ -171,9 +167,7 @@ const createTakePattern = ( } }) - const promises: (Promise | Promise<[AnyAction, S, S]>)[] = [ - tuplePromise, - ] + const promises: (Promise | Promise<[Action, S, S]>)[] = [tuplePromise] if (timeout != null) { promises.push( @@ -241,7 +235,7 @@ export const createListenerEntry: TypedCreateListenerEntry = ( } const cancelActiveListeners = ( - entry: ListenerEntry> + entry: ListenerEntry> ) => { entry.pending.forEach((controller) => { abortControllerWithReason(controller, listenerCancelled) @@ -309,7 +303,7 @@ const defaultErrorHandler: ListenerErrorHandler = (...args: unknown[]) => { */ export function createListenerMiddleware< S = unknown, - D extends Dispatch = ThunkDispatch, + D extends Dispatch = ThunkDispatch, ExtraArgument = unknown >(middlewareOptions: CreateListenerMiddlewareOptions = {}) { const listenerMap = new Map() @@ -367,8 +361,8 @@ export function createListenerMiddleware< } const notifyListener = async ( - entry: ListenerEntry>, - action: AnyAction, + entry: ListenerEntry>, + action: unknown, api: MiddlewareAPI, getOriginalState: () => S ) => { diff --git a/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts b/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts index db9d5ae571..73e039cad8 100644 --- a/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts +++ b/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts @@ -6,7 +6,7 @@ import { } from '@reduxjs/toolkit' import { vi } from 'vitest' -import type { AnyAction, PayloadAction, Action } from '@reduxjs/toolkit' +import type { PayloadAction } from '@reduxjs/toolkit' import { createListenerMiddleware, TaskAbortError } from '../index' diff --git a/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts b/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts index e268d570b2..72b1a7be2b 100644 --- a/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts +++ b/packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts @@ -3,11 +3,12 @@ import { createAction, createSlice, isAnyOf, + isFSA, } from '@reduxjs/toolkit' import type { Mock } from 'vitest' import { vi } from 'vitest' -import type { AnyAction, PayloadAction, Action } from '@reduxjs/toolkit' +import type { Action, UnknownAction, PayloadAction } from '@reduxjs/toolkit' import { createListenerMiddleware, @@ -196,7 +197,7 @@ describe('createListenerMiddleware', () => { > typedAddListener({ - matcher: (action: AnyAction): action is AnyAction => true, + matcher: (action): action is Action => true, effect: (action, listenerApi) => { foundExtra = listenerApi.extra expectType(listenerApi.extra) @@ -272,7 +273,7 @@ describe('createListenerMiddleware', () => { }) test('can subscribe with a string action type', () => { - const effect = vi.fn((_: AnyAction) => {}) + const effect = vi.fn((_: UnknownAction) => {}) store.dispatch( addListener({ @@ -291,7 +292,7 @@ describe('createListenerMiddleware', () => { }) test('can subscribe with a matcher function', () => { - const effect = vi.fn((_: AnyAction) => {}) + const effect = vi.fn((_: UnknownAction) => {}) const isAction1Or2 = isAnyOf(testAction1, testAction2) @@ -574,7 +575,7 @@ describe('createListenerMiddleware', () => { 'add and remove listener with "%s" param correctly', (_, params) => { const effect: ListenerEffect< - AnyAction, + UnknownAction, typeof store.getState, typeof store.dispatch > = vi.fn() @@ -591,7 +592,7 @@ describe('createListenerMiddleware', () => { } ) - const unforwardedActions: [string, AnyAction][] = [ + const unforwardedActions: [string, UnknownAction][] = [ [ 'addListener', addListener({ actionCreator: testAction1, effect: noop }), @@ -1073,7 +1074,7 @@ describe('createListenerMiddleware', () => { typedAddListener({ predicate: incrementByAmount.match, - async effect(_: AnyAction, listenerApi) { + async effect(_: UnknownAction, listenerApi) { result = await listenerApi.take(increment.match) }, }) @@ -1341,7 +1342,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectUnknown(currentState) expectUnknown(previousState) return true @@ -1361,7 +1362,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectUnknown(currentState) expectUnknown(previousState) return true @@ -1387,7 +1388,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectUnknown(currentState) expectUnknown(previousState) return true @@ -1447,7 +1448,7 @@ describe('createListenerMiddleware', () => { currentState, previousState ): action is PayloadAction => { - return typeof action.payload === 'boolean' + return isFSA(action) && typeof action.payload === 'boolean' }, effect: (action, listenerApi) => { expectExactType>(action) @@ -1456,10 +1457,10 @@ describe('createListenerMiddleware', () => { startListening({ predicate: (action, currentState) => { - return typeof action.payload === 'number' + return isFSA(action) && typeof action.payload === 'number' }, effect: (action, listenerApi) => { - expectExactType(action) + expectExactType(action) }, }) @@ -1499,7 +1500,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectNotAny(currentState) expectNotAny(previousState) expectExactType(currentState) @@ -1520,7 +1521,7 @@ describe('createListenerMiddleware', () => { typedMiddleware.startListening({ // TODO Why won't this infer the listener's `action` with implicit argument types? predicate: ( - action: AnyAction, + action: UnknownAction, currentState: CounterState ): action is PayloadAction => { expectNotAny(currentState) @@ -1581,7 +1582,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectNotAny(currentState) expectNotAny(previousState) expectExactType(currentState) @@ -1611,7 +1612,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectNotAny(currentState) expectNotAny(previousState) expectExactType(currentState) @@ -1648,7 +1649,7 @@ describe('createListenerMiddleware', () => { action, currentState, previousState - ): action is AnyAction => { + ): action is UnknownAction => { expectNotAny(currentState) expectNotAny(previousState) expectExactType(currentState) diff --git a/packages/toolkit/src/listenerMiddleware/types.ts b/packages/toolkit/src/listenerMiddleware/types.ts index 88479b5f56..d6b2f7f2b3 100644 --- a/packages/toolkit/src/listenerMiddleware/types.ts +++ b/packages/toolkit/src/listenerMiddleware/types.ts @@ -1,10 +1,10 @@ import type { PayloadAction, BaseActionCreator } from '../createAction' import type { Dispatch as ReduxDispatch, - AnyAction, MiddlewareAPI, Middleware, Action as ReduxAction, + UnknownAction, } from 'redux' import type { ThunkDispatch } from 'redux-thunk' import type { TaskAbortError } from './exceptions' @@ -28,14 +28,14 @@ export interface TypedActionCreator { /** @internal */ export type AnyListenerPredicate = ( - action: AnyAction, + action: UnknownAction, currentState: State, originalState: State ) => boolean /** @public */ -export type ListenerPredicate = ( - action: AnyAction, +export type ListenerPredicate = ( + action: UnknownAction, currentState: State, originalState: State ) => action is Action @@ -137,13 +137,13 @@ export interface ForkOptions { * If true, causes the parent task to not be marked as complete until * all autoJoined forks have completed or failed. */ - autoJoin: boolean; + autoJoin: boolean } /** @public */ export interface ListenerEffectAPI< State, - Dispatch extends ReduxDispatch, + Dispatch extends ReduxDispatch, ExtraArgument = unknown > extends MiddlewareAPI { /** @@ -261,9 +261,9 @@ export interface ListenerEffectAPI< /** @public */ export type ListenerEffect< - Action extends AnyAction, + Action extends ReduxAction, State, - Dispatch extends ReduxDispatch, + Dispatch extends ReduxDispatch, ExtraArgument = unknown > = ( action: Action, @@ -303,10 +303,10 @@ export interface CreateListenerMiddlewareOptions { /** @public */ export type ListenerMiddleware< State = unknown, - Dispatch extends ThunkDispatch = ThunkDispatch< + Dispatch extends ThunkDispatch = ThunkDispatch< State, unknown, - AnyAction + UnknownAction >, ExtraArgument = unknown > = Middleware< @@ -320,10 +320,10 @@ export type ListenerMiddleware< /** @public */ export interface ListenerMiddlewareInstance< State = unknown, - Dispatch extends ThunkDispatch = ThunkDispatch< + Dispatch extends ThunkDispatch = ThunkDispatch< State, unknown, - AnyAction + UnknownAction >, ExtraArgument = unknown > { @@ -351,7 +351,7 @@ export type TakePatternOutputWithoutTimeout< Predicate extends AnyListenerPredicate > = Predicate extends MatchFunction ? Promise<[Action, State, State]> - : Promise<[AnyAction, State, State]> + : Promise<[UnknownAction, State, State]> /** @public */ export type TakePatternOutputWithTimeout< @@ -359,7 +359,7 @@ export type TakePatternOutputWithTimeout< Predicate extends AnyListenerPredicate > = Predicate extends MatchFunction ? Promise<[Action, State, State] | null> - : Promise<[AnyAction, State, State] | null> + : Promise<[UnknownAction, State, State] | null> /** @public */ export interface TakePattern { @@ -393,12 +393,12 @@ export type UnsubscribeListener = ( export interface AddListenerOverloads< Return, State = unknown, - Dispatch extends ReduxDispatch = ThunkDispatch, + Dispatch extends ReduxDispatch = ThunkDispatch, ExtraArgument = unknown, AdditionalOptions = unknown > { /** Accepts a "listener predicate" that is also a TS type predicate for the action*/ - >( + >( options: { actionCreator?: never type?: never @@ -436,7 +436,7 @@ export interface AddListenerOverloads< ): Return /** Accepts an RTK matcher function, such as `incrementByAmount.match` */ - >( + >( options: { actionCreator?: never type?: never @@ -453,7 +453,7 @@ export interface AddListenerOverloads< type?: never matcher?: never predicate: LP - effect: ListenerEffect + effect: ListenerEffect } & AdditionalOptions ): Return } @@ -461,7 +461,7 @@ export interface AddListenerOverloads< /** @public */ export type RemoveListenerOverloads< State = unknown, - Dispatch extends ReduxDispatch = ThunkDispatch + Dispatch extends ReduxDispatch = ThunkDispatch > = AddListenerOverloads< boolean, State, @@ -472,9 +472,9 @@ export type RemoveListenerOverloads< /** @public */ export interface RemoveListenerAction< - Action extends AnyAction, + Action extends UnknownAction, State, - Dispatch extends ReduxDispatch + Dispatch extends ReduxDispatch > { type: 'listenerMiddleware/remove' payload: { @@ -488,11 +488,7 @@ export interface RemoveListenerAction< * A "pre-typed" version of `addListenerAction`, so the listener args are well-typed */ export type TypedAddListener< State, - Dispatch extends ReduxDispatch = ThunkDispatch< - State, - unknown, - AnyAction - >, + Dispatch extends ReduxDispatch = ThunkDispatch, ExtraArgument = unknown, Payload = ListenerEntry, T extends string = 'listenerMiddleware/add' @@ -509,11 +505,7 @@ export type TypedAddListener< * A "pre-typed" version of `removeListenerAction`, so the listener args are well-typed */ export type TypedRemoveListener< State, - Dispatch extends ReduxDispatch = ThunkDispatch< - State, - unknown, - AnyAction - >, + Dispatch extends ReduxDispatch = ThunkDispatch, Payload = ListenerEntry, T extends string = 'listenerMiddleware/remove' > = BaseActionCreator & @@ -530,11 +522,7 @@ export type TypedRemoveListener< * A "pre-typed" version of `middleware.startListening`, so the listener args are well-typed */ export type TypedStartListening< State, - Dispatch extends ReduxDispatch = ThunkDispatch< - State, - unknown, - AnyAction - >, + Dispatch extends ReduxDispatch = ThunkDispatch, ExtraArgument = unknown > = AddListenerOverloads @@ -542,22 +530,14 @@ export type TypedStartListening< * A "pre-typed" version of `middleware.stopListening`, so the listener args are well-typed */ export type TypedStopListening< State, - Dispatch extends ReduxDispatch = ThunkDispatch< - State, - unknown, - AnyAction - > + Dispatch extends ReduxDispatch = ThunkDispatch > = RemoveListenerOverloads /** @public * A "pre-typed" version of `createListenerEntry`, so the listener args are well-typed */ export type TypedCreateListenerEntry< State, - Dispatch extends ReduxDispatch = ThunkDispatch< - State, - unknown, - AnyAction - > + Dispatch extends ReduxDispatch = ThunkDispatch > = AddListenerOverloads, State, Dispatch> /** @@ -567,14 +547,14 @@ export type TypedCreateListenerEntry< /** @internal An single listener entry */ export type ListenerEntry< State = unknown, - Dispatch extends ReduxDispatch = ReduxDispatch + Dispatch extends ReduxDispatch = ReduxDispatch > = { id: string effect: ListenerEffect unsubscribe: () => void pending: Set type?: string - predicate: ListenerPredicate + predicate: ListenerPredicate } /** diff --git a/packages/toolkit/src/mapBuilders.ts b/packages/toolkit/src/mapBuilders.ts index 559e234d03..1c8ed426d2 100644 --- a/packages/toolkit/src/mapBuilders.ts +++ b/packages/toolkit/src/mapBuilders.ts @@ -1,4 +1,4 @@ -import type { Action, AnyAction } from 'redux' +import type { Action } from 'redux' import type { CaseReducer, CaseReducers, @@ -56,7 +56,7 @@ import { createAction, createReducer, AsyncThunk, - AnyAction, + UnknownAction, } from "@reduxjs/toolkit"; type GenericAsyncThunk = AsyncThunk; @@ -68,8 +68,8 @@ type FulfilledAction = ReturnType; const initialState: Record = {}; const resetAction = createAction("reset-tracked-loading-state"); -function isPendingAction(action: AnyAction): action is PendingAction { - return action.type.endsWith("/pending"); +function isPendingAction(action: UnknownAction): action is PendingAction { + return typeof action.type === "string" && action.type.endsWith("/pending"); } const reducer = createReducer(initialState, (builder) => { @@ -98,7 +98,7 @@ const reducer = createReducer(initialState, (builder) => { */ addMatcher( matcher: TypeGuard | ((action: any) => boolean), - reducer: CaseReducer + reducer: CaseReducer ): Omit, 'addCase'> /** @@ -120,7 +120,7 @@ const reducer = createReducer(initialState, builder => { }) ``` */ - addDefaultCase(reducer: CaseReducer): {} + addDefaultCase(reducer: CaseReducer): {} } export function executeReducerBuilderCallback( @@ -128,11 +128,11 @@ export function executeReducerBuilderCallback( ): [ CaseReducers, ActionMatcherDescriptionCollection, - CaseReducer | undefined + CaseReducer | undefined ] { const actionsMap: CaseReducers = {} const actionMatchers: ActionMatcherDescriptionCollection = [] - let defaultCaseReducer: CaseReducer | undefined + let defaultCaseReducer: CaseReducer | undefined const builder = { addCase( typeOrActionCreator: string | TypedActionCreator, @@ -169,7 +169,7 @@ export function executeReducerBuilderCallback( }, addMatcher( matcher: TypeGuard, - reducer: CaseReducer + reducer: CaseReducer ) { if (process.env.NODE_ENV !== 'production') { if (defaultCaseReducer) { @@ -181,7 +181,7 @@ export function executeReducerBuilderCallback( actionMatchers.push({ matcher, reducer }) return builder }, - addDefaultCase(reducer: CaseReducer) { + addDefaultCase(reducer: CaseReducer) { if (process.env.NODE_ENV !== 'production') { if (defaultCaseReducer) { throw new Error('`builder.addDefaultCase` can only be called once') diff --git a/packages/toolkit/src/query/apiTypes.ts b/packages/toolkit/src/query/apiTypes.ts index e068250ce6..3df10b2741 100644 --- a/packages/toolkit/src/query/apiTypes.ts +++ b/packages/toolkit/src/query/apiTypes.ts @@ -13,7 +13,7 @@ import type { CoreModule } from './core/module' import type { CreateApiOptions } from './createApi' import type { BaseQueryFn } from './baseQueryTypes' import type { CombinedState } from './core/apiState' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' export interface ApiModules< // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -61,9 +61,9 @@ export interface ApiContext { endpointDefinitions: Definitions batch(cb: () => void): void extractRehydrationInfo: ( - action: AnyAction + action: UnknownAction ) => CombinedState | undefined - hasRehydrationInfo: (action: AnyAction) => boolean + hasRehydrationInfo: (action: UnknownAction) => boolean } export type Api< diff --git a/packages/toolkit/src/query/core/buildInitiate.ts b/packages/toolkit/src/query/core/buildInitiate.ts index a26aaf0b2a..2811f42681 100644 --- a/packages/toolkit/src/query/core/buildInitiate.ts +++ b/packages/toolkit/src/query/core/buildInitiate.ts @@ -7,7 +7,11 @@ import type { } from '../endpointDefinitions' import { DefinitionType, isQueryDefinition } from '../endpointDefinitions' import type { QueryThunk, MutationThunk, QueryThunkArg } from './buildThunks' -import type { AnyAction, ThunkAction, SerializedError } from '@reduxjs/toolkit' +import type { + UnknownAction, + ThunkAction, + SerializedError, +} from '@reduxjs/toolkit' import type { SubscriptionOptions, RootState } from './apiState' import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs' import type { Api, ApiContext } from '../apiTypes' @@ -51,7 +55,7 @@ type StartQueryActionCreator< > = ( arg: QueryArgFrom, options?: StartQueryActionCreatorOptions -) => ThunkAction, any, any, AnyAction> +) => ThunkAction, any, any, UnknownAction> export type QueryActionCreatorResult< D extends QueryDefinition @@ -81,7 +85,7 @@ type StartMutationActionCreator< track?: boolean fixedCacheKey?: string } -) => ThunkAction, any, any, AnyAction> +) => ThunkAction, any, any, UnknownAction> export type MutationActionCreatorResult< D extends MutationDefinition diff --git a/packages/toolkit/src/query/core/buildMiddleware/batchActions.ts b/packages/toolkit/src/query/core/buildMiddleware/batchActions.ts index d50bafead4..0bd2a73515 100644 --- a/packages/toolkit/src/query/core/buildMiddleware/batchActions.ts +++ b/packages/toolkit/src/query/core/buildMiddleware/batchActions.ts @@ -1,13 +1,7 @@ -import type { QueryThunk, RejectedAction } from '../buildThunks' import type { InternalHandlerBuilder } from './types' -import type { - SubscriptionState, - QuerySubstateIdentifier, - Subscribers, -} from '../apiState' +import type { SubscriptionState } from '../apiState' import { produceWithPatches } from 'immer' -import type { AnyAction } from '@reduxjs/toolkit' -import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import type { Action } from '@reduxjs/toolkit' export const buildBatchedActionsHandler: InternalHandlerBuilder< [actionShouldContinue: boolean, subscriptionExists: boolean] @@ -26,7 +20,7 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder< // This is done to speed up perf when loading many components const actuallyMutateSubscriptions = ( mutableState: SubscriptionState, - action: AnyAction + action: Action ) => { if (updateSubscriptionOptions.match(action)) { const { queryCacheKey, requestId, options } = action.payload @@ -75,7 +69,10 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder< return false } - return (action, mwApi) => { + return ( + action, + mwApi + ): [actionShouldContinue: boolean, hasSubscription: boolean] => { if (!previousSubscriptions) { // Initialize it the first time this handler runs previousSubscriptions = JSON.parse( @@ -126,7 +123,8 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder< } const isSubscriptionSliceAction = - !!action.type?.startsWith(subscriptionsPrefix) + typeof action.type == 'string' && + !!action.type.startsWith(subscriptionsPrefix) const isAdditionalSubscriptionAction = queryThunk.rejected.match(action) && action.meta.condition && diff --git a/packages/toolkit/src/query/core/buildMiddleware/cacheLifecycle.ts b/packages/toolkit/src/query/core/buildMiddleware/cacheLifecycle.ts index 923eb79d56..d83a25b15f 100644 --- a/packages/toolkit/src/query/core/buildMiddleware/cacheLifecycle.ts +++ b/packages/toolkit/src/query/core/buildMiddleware/cacheLifecycle.ts @@ -1,5 +1,5 @@ import { isAsyncThunkAction, isFulfilled } from '@reduxjs/toolkit' -import type { AnyAction } from 'redux' +import type { UnknownAction } from 'redux' import type { ThunkDispatch } from 'redux-thunk' import type { BaseQueryFn, BaseQueryMeta } from '../../baseQueryTypes' import { DefinitionType } from '../../endpointDefinitions' @@ -65,7 +65,7 @@ declare module '../../endpointDefinitions' { /** * The dispatch method for the store */ - dispatch: ThunkDispatch + dispatch: ThunkDispatch /** * A method to get the current state */ diff --git a/packages/toolkit/src/query/core/buildMiddleware/index.ts b/packages/toolkit/src/query/core/buildMiddleware/index.ts index 1ef1103fe3..2ef7c7f347 100644 --- a/packages/toolkit/src/query/core/buildMiddleware/index.ts +++ b/packages/toolkit/src/query/core/buildMiddleware/index.ts @@ -1,4 +1,9 @@ -import type { AnyAction, Middleware, ThunkDispatch } from '@reduxjs/toolkit' +import type { + Action, + Middleware, + ThunkDispatch, + UnknownAction, +} from '@reduxjs/toolkit' import { isAction, createAction } from '@reduxjs/toolkit' import type { @@ -35,13 +40,8 @@ export function buildMiddleware< >(`${reducerPath}/invalidateTags`), } - const isThisApiSliceAction = (action: AnyAction) => { - return ( - !!action && - typeof action.type === 'string' && - action.type.startsWith(`${reducerPath}/`) - ) - } + const isThisApiSliceAction = (action: Action) => + action.type.startsWith(`${reducerPath}/`) const handlerBuilders: InternalHandlerBuilder[] = [ buildDevCheckHandler, @@ -55,7 +55,7 @@ export function buildMiddleware< const middleware: Middleware< {}, RootState, - ThunkDispatch + ThunkDispatch > = (mwApi) => { let initialized = false diff --git a/packages/toolkit/src/query/core/buildMiddleware/types.ts b/packages/toolkit/src/query/core/buildMiddleware/types.ts index 78e6a53e90..c7e4e52e02 100644 --- a/packages/toolkit/src/query/core/buildMiddleware/types.ts +++ b/packages/toolkit/src/query/core/buildMiddleware/types.ts @@ -1,10 +1,10 @@ import type { - AnyAction, + Action, AsyncThunkAction, - Dispatch, Middleware, MiddlewareAPI, ThunkDispatch, + UnknownAction, } from '@reduxjs/toolkit' import type { Api, ApiContext } from '../../apiTypes' @@ -46,7 +46,7 @@ export interface BuildMiddlewareInput< } export type SubMiddlewareApi = MiddlewareAPI< - ThunkDispatch, + ThunkDispatch, RootState > @@ -68,13 +68,13 @@ export type SubMiddlewareBuilder = ( ) => Middleware< {}, RootState, - ThunkDispatch + ThunkDispatch > type MwNext = Parameters>[0] export type ApiMiddlewareInternalHandler = ( - action: AnyAction, + action: Action, mwApi: SubMiddlewareApi & { next: MwNext }, prevState: RootState ) => Return diff --git a/packages/toolkit/src/query/core/buildSlice.ts b/packages/toolkit/src/query/core/buildSlice.ts index b32531eb49..ef37ca3023 100644 --- a/packages/toolkit/src/query/core/buildSlice.ts +++ b/packages/toolkit/src/query/core/buildSlice.ts @@ -1,4 +1,4 @@ -import type { AnyAction, PayloadAction } from '@reduxjs/toolkit' +import type { PayloadAction, UnknownAction } from '@reduxjs/toolkit' import { combineReducers, createAction, diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index 458b9edd44..d0e609302c 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -23,7 +23,11 @@ import type { } from '../endpointDefinitions' import { isQueryDefinition } from '../endpointDefinitions' import { calculateProvidedBy } from '../endpointDefinitions' -import type { AsyncThunkPayloadCreator, Draft } from '@reduxjs/toolkit' +import type { + AsyncThunkPayloadCreator, + Draft, + UnknownAction, +} from '@reduxjs/toolkit' import { isAllOf, isFulfilled, @@ -33,12 +37,7 @@ import { } from '@reduxjs/toolkit' import type { Patch } from 'immer' import { isDraftable, produceWithPatches } from 'immer' -import type { - AnyAction, - ThunkAction, - ThunkDispatch, - AsyncThunk, -} from '@reduxjs/toolkit' +import type { ThunkAction, ThunkDispatch, AsyncThunk } from '@reduxjs/toolkit' import { createAsyncThunk, SHOULD_AUTOBATCH } from '@reduxjs/toolkit' import { HandledError } from '../HandledError' @@ -165,7 +164,7 @@ export type PatchQueryDataThunk< endpointName: EndpointName, args: QueryArgFrom, patches: readonly Patch[] -) => ThunkAction +) => ThunkAction export type UpdateQueryDataThunk< Definitions extends EndpointDefinitions, @@ -174,7 +173,7 @@ export type UpdateQueryDataThunk< endpointName: EndpointName, args: QueryArgFrom, updateRecipe: Recipe> -) => ThunkAction +) => ThunkAction export type UpsertQueryDataThunk< Definitions extends EndpointDefinitions, @@ -191,7 +190,7 @@ export type UpsertQueryDataThunk< >, PartialState, any, - AnyAction + UnknownAction > /** @@ -553,7 +552,7 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` endpointName: EndpointName, arg: any, options: PrefetchOptions - ): ThunkAction => + ): ThunkAction => (dispatch: ThunkDispatch, getState: () => any) => { const force = hasTheForce(options) && options.force const maxAge = hasMaxAge(options) && options.ifOlderThan @@ -588,7 +587,7 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` } function matchesEndpoint(endpointName: string) { - return (action: any): action is AnyAction => + return (action: any): action is UnknownAction => action?.meta?.arg?.endpointName === endpointName } diff --git a/packages/toolkit/src/query/core/module.ts b/packages/toolkit/src/query/core/module.ts index 874fc34fa0..0b7d85b582 100644 --- a/packages/toolkit/src/query/core/module.ts +++ b/packages/toolkit/src/query/core/module.ts @@ -9,11 +9,11 @@ import type { import { buildThunks } from './buildThunks' import type { ActionCreatorWithPayload, - AnyAction, Middleware, Reducer, ThunkAction, ThunkDispatch, + UnknownAction, } from '@reduxjs/toolkit' import type { EndpointDefinitions, @@ -72,7 +72,7 @@ export type CoreModule = | ReferenceCacheCollection export interface ThunkWithReturnValue - extends ThunkAction {} + extends ThunkAction {} declare module '../apiTypes' { export interface ApiModules< @@ -116,7 +116,7 @@ declare module '../apiTypes' { */ reducer: Reducer< CombinedState, - AnyAction + UnknownAction > /** * This is a standard redux middleware and is responsible for things like polling, garbage collection and a handful of other things. Make sure it's included in your store. @@ -134,7 +134,7 @@ declare module '../apiTypes' { middleware: Middleware< {}, RootState, - ThunkDispatch + ThunkDispatch > /** * A collection of utility thunks for various situations. @@ -221,7 +221,7 @@ declare module '../apiTypes' { endpointName: EndpointName, arg: QueryArgFrom, options: PrefetchOptions - ): ThunkAction + ): ThunkAction /** * A Redux thunk action creator that, when dispatched, creates and applies a set of JSON diff/patch objects to the current state. This immediately updates the Redux state with those changes. * diff --git a/packages/toolkit/src/query/createApi.ts b/packages/toolkit/src/query/createApi.ts index 5ff83e860e..67cebe9b9f 100644 --- a/packages/toolkit/src/query/createApi.ts +++ b/packages/toolkit/src/query/createApi.ts @@ -9,7 +9,7 @@ import type { } from './endpointDefinitions' import { DefinitionType, isQueryDefinition } from './endpointDefinitions' import { nanoid } from '@reduxjs/toolkit' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import type { NoInfer } from './tsHelpers' import { defaultMemoize } from 'reselect' @@ -178,7 +178,7 @@ export interface CreateApiOptions< * ``` */ extractRehydrationInfo?: ( - action: AnyAction, + action: UnknownAction, { reducerPath, }: { @@ -236,7 +236,7 @@ export function buildCreateApi, ...Module[]]>( ...modules: Modules ): CreateApi { return function baseCreateApi(options) { - const extractRehydrationInfo = defaultMemoize((action: AnyAction) => + const extractRehydrationInfo = defaultMemoize((action: UnknownAction) => options.extractRehydrationInfo?.(action, { reducerPath: (options.reducerPath ?? 'api') as any, }) diff --git a/packages/toolkit/src/query/endpointDefinitions.ts b/packages/toolkit/src/query/endpointDefinitions.ts index d0411f3ffb..7ba8dcf815 100644 --- a/packages/toolkit/src/query/endpointDefinitions.ts +++ b/packages/toolkit/src/query/endpointDefinitions.ts @@ -1,4 +1,3 @@ -import type { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import type { SerializeQueryArgs } from './defaultSerializeQueryArgs' import type { QuerySubState, RootState } from './core/apiState' import type { diff --git a/packages/toolkit/src/query/react/buildHooks.ts b/packages/toolkit/src/query/react/buildHooks.ts index c658c6388d..d8717dbbac 100644 --- a/packages/toolkit/src/query/react/buildHooks.ts +++ b/packages/toolkit/src/query/react/buildHooks.ts @@ -1,4 +1,9 @@ -import type { AnyAction, Selector, ThunkAction, ThunkDispatch } from '@reduxjs/toolkit' +import type { + UnknownAction, + Selector, + ThunkAction, + ThunkDispatch, +} from '@reduxjs/toolkit' import { createSelector } from '@reduxjs/toolkit' import type { DependencyList } from 'react' import { @@ -566,7 +571,7 @@ type GenericPrefetchThunk = ( endpointName: any, arg: any, options: PrefetchOptions -) => ThunkAction +) => ThunkAction /** * @@ -651,7 +656,7 @@ export function buildHooks({ endpointName: EndpointName, defaultOptions?: PrefetchOptions ) { - const dispatch = useDispatch>() + const dispatch = useDispatch>() const stableDefaultOptions = useShallowStableValue(defaultOptions) return useCallback( @@ -681,7 +686,7 @@ export function buildHooks({ QueryDefinition, Definitions > - const dispatch = useDispatch>() + const dispatch = useDispatch>() const stableArg = useStableQueryArgs( skip ? skipToken : arg, // Even if the user provided a per-endpoint `serializeQueryArgs` with @@ -815,7 +820,7 @@ export function buildHooks({ QueryDefinition, Definitions > - const dispatch = useDispatch>() + const dispatch = useDispatch>() const [arg, setArg] = useState(UNINITIALIZED_VALUE) const promiseRef = useRef | undefined>() @@ -986,7 +991,7 @@ export function buildHooks({ MutationDefinition, Definitions > - const dispatch = useDispatch>() + const dispatch = useDispatch>() const [promise, setPromise] = useState>() useEffect( diff --git a/packages/toolkit/src/query/tests/buildHooks.test.tsx b/packages/toolkit/src/query/tests/buildHooks.test.tsx index 6cd0fb3510..39f93d145f 100644 --- a/packages/toolkit/src/query/tests/buildHooks.test.tsx +++ b/packages/toolkit/src/query/tests/buildHooks.test.tsx @@ -32,7 +32,7 @@ import { waitMs, } from './helpers' import { server } from './mocks/server' -import type { AnyAction } from 'redux' +import type { UnknownAction } from 'redux' import type { SubscriptionOptions } from '@reduxjs/toolkit/dist/query/core/apiState' import type { SerializedError } from '@reduxjs/toolkit' import { createListenerMiddleware, configureStore } from '@reduxjs/toolkit' @@ -126,7 +126,7 @@ const api = createApi({ const listenerMiddleware = createListenerMiddleware() -let actions: AnyAction[] = [] +let actions: UnknownAction[] = [] const storeRef = setupApiStore( api, @@ -1748,7 +1748,7 @@ describe('hooks tests', () => { }) const storeRef = setupApiStore(api, { - actions(state: AnyAction[] = [], action: AnyAction) { + actions(state: UnknownAction[] = [], action: UnknownAction) { return [...state, action] }, }) diff --git a/packages/toolkit/src/query/tests/cleanup.test.tsx b/packages/toolkit/src/query/tests/cleanup.test.tsx index 75c0f4c6e0..36870e11db 100644 --- a/packages/toolkit/src/query/tests/cleanup.test.tsx +++ b/packages/toolkit/src/query/tests/cleanup.test.tsx @@ -162,7 +162,7 @@ test('Minimizes the number of subscription dispatches when multiple components a let getSubscriptionsA = () => storeRef.store.getState().api.subscriptions['a(undefined)'] - let actionTypes: string[] = [] + let actionTypes: unknown[] = [] listenerMiddleware.startListening({ predicate: () => true, diff --git a/packages/toolkit/src/query/tests/errorHandling.test.tsx b/packages/toolkit/src/query/tests/errorHandling.test.tsx index c33f724467..96a3e0cc63 100644 --- a/packages/toolkit/src/query/tests/errorHandling.test.tsx +++ b/packages/toolkit/src/query/tests/errorHandling.test.tsx @@ -15,7 +15,7 @@ import { renderHook, } from '@testing-library/react' import { useDispatch } from 'react-redux' -import type { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import type { UnknownAction, ThunkDispatch } from '@reduxjs/toolkit' import type { BaseQueryApi } from '../baseQueryTypes' const baseQuery = fetchBaseQuery({ baseUrl: 'https://example.com' }) @@ -556,7 +556,11 @@ describe('error handling in a component', () => { test(`an un-subscribed mutation will still return something useful (success case, track: ${track})`, async () => { const hook = renderHook(useDispatch, { wrapper: storeRef.wrapper }) - const dispatch = hook.result.current as ThunkDispatch + const dispatch = hook.result.current as ThunkDispatch< + any, + any, + UnknownAction + > let mutationqueryFulfilled: ReturnType< ReturnType > @@ -574,7 +578,11 @@ describe('error handling in a component', () => { test(`an un-subscribed mutation will still return something useful (error case, track: ${track})`, async () => { const hook = renderHook(useDispatch, { wrapper: storeRef.wrapper }) - const dispatch = hook.result.current as ThunkDispatch + const dispatch = hook.result.current as ThunkDispatch< + any, + any, + UnknownAction + > let mutationqueryFulfilled: ReturnType< ReturnType > @@ -594,7 +602,11 @@ describe('error handling in a component', () => { test(`an un-subscribed mutation will still be unwrappable (success case), track: ${track}`, async () => { const hook = renderHook(useDispatch, { wrapper: storeRef.wrapper }) - const dispatch = hook.result.current as ThunkDispatch + const dispatch = hook.result.current as ThunkDispatch< + any, + any, + UnknownAction + > let mutationqueryFulfilled: ReturnType< ReturnType > @@ -612,7 +624,11 @@ describe('error handling in a component', () => { test(`an un-subscribed mutation will still be unwrappable (error case, track: ${track})`, async () => { const hook = renderHook(useDispatch, { wrapper: storeRef.wrapper }) - const dispatch = hook.result.current as ThunkDispatch + const dispatch = hook.result.current as ThunkDispatch< + any, + any, + UnknownAction + > let mutationqueryFulfilled: ReturnType< ReturnType > diff --git a/packages/toolkit/src/query/tests/helpers.tsx b/packages/toolkit/src/query/tests/helpers.tsx index d566b4c0fc..01c2a2260c 100644 --- a/packages/toolkit/src/query/tests/helpers.tsx +++ b/packages/toolkit/src/query/tests/helpers.tsx @@ -1,6 +1,6 @@ import React, { useCallback } from 'react' import type { - AnyAction, + UnknownAction, EnhancedStore, Middleware, Store, @@ -108,7 +108,7 @@ declare global { expect.extend({ toMatchSequence( - _actions: AnyAction[], + _actions: UnknownAction[], ...matchers: Array<(arg: any) => boolean> ) { const actions = _actions.concat() @@ -179,7 +179,7 @@ ${expectedOutput} }) export const actionsReducer = { - actions: (state: AnyAction[] = [], action: AnyAction) => { + actions: (state: UnknownAction[] = [], action: UnknownAction) => { return [...state, action] }, } @@ -230,11 +230,15 @@ export function setupApiStore< [K in keyof R]: ReturnType } type StoreType = EnhancedStore< - State, - AnyAction, - ReturnType extends EnhancedStore - ? E - : [] + { + api: ReturnType + } & { + [K in keyof R]: ReturnType + }, + UnknownAction, + ReturnType extends EnhancedStore + ? M + : never > const initialStore = getStore() as StoreType diff --git a/packages/toolkit/src/tests/configureStore.typetest.ts b/packages/toolkit/src/tests/configureStore.typetest.ts index 5ee4cbec0c..8291d570ce 100644 --- a/packages/toolkit/src/tests/configureStore.typetest.ts +++ b/packages/toolkit/src/tests/configureStore.typetest.ts @@ -1,7 +1,7 @@ /* eslint-disable no-lone-blocks */ import type { Dispatch, - AnyAction, + UnknownAction, Middleware, Reducer, Store, @@ -48,10 +48,10 @@ const _anyMiddleware: any = () => () => () => {} { const reducer: Reducer = () => 0 const store = configureStore({ reducer }) - const numberStore: Store = store + const numberStore: Store = store // @ts-expect-error - const stringStore: Store = store + const stringStore: Store = store } /* @@ -156,7 +156,7 @@ const _anyMiddleware: any = () => () => () => {} enhancers: [enhancer], }) - expectType>( + expectType>( store.dispatch ) } @@ -210,7 +210,7 @@ const _anyMiddleware: any = () => () => () => {} .concat(somePropertyStoreEnhancer), }) - expectType>( + expectType>( store.dispatch ) expectType(storeWithCallback.someProperty) @@ -588,18 +588,28 @@ const _anyMiddleware: any = () => () => () => {} void, {}, undefined, - AnyAction + UnknownAction >) // `null` for the `extra` generic was previously documented in the RTK "Advanced Tutorial", but // is a bad pattern and users should use `unknown` instead // @ts-expect-error - store.dispatch(function () {} as ThunkAction) + store.dispatch(function () {} as ThunkAction) // unknown is the best way to type a ThunkAction if you do not care // about the value of the extraArgument, as it will always work with every // ThunkMiddleware, no matter the actual extraArgument type - store.dispatch(function () {} as ThunkAction) + store.dispatch(function () {} as ThunkAction< + void, + {}, + unknown, + UnknownAction + >) // @ts-expect-error - store.dispatch(function () {} as ThunkAction) + store.dispatch(function () {} as ThunkAction< + void, + {}, + boolean, + UnknownAction + >) } /** @@ -805,8 +815,8 @@ const _anyMiddleware: any = () => () => () => {} // the thunk middleware type kicks in and TS thinks a plain action is being returned expectType< ((action: Action<'actionListenerMiddleware/add'>) => Unsubscribe) & - ThunkDispatch & - Dispatch + ThunkDispatch & + Dispatch >(store.dispatch) const unsubscribe = store.dispatch({ diff --git a/packages/toolkit/src/tests/createAction.typetest.tsx b/packages/toolkit/src/tests/createAction.typetest.tsx index f0981335a5..991d8c0f63 100644 --- a/packages/toolkit/src/tests/createAction.typetest.tsx +++ b/packages/toolkit/src/tests/createAction.typetest.tsx @@ -1,5 +1,5 @@ import React from 'react' -import type { Action, AnyAction, ActionCreator } from 'redux' +import type { Action, UnknownAction, ActionCreator } from 'redux' import type { PayloadAction, PayloadActionCreator, @@ -92,7 +92,7 @@ import { expectType } from './helpers' }), { type: 'action' } ) as PayloadActionCreator - const actionCreator: ActionCreator = payloadActionCreator + const actionCreator: ActionCreator = payloadActionCreator const payloadActionCreator2 = Object.assign( (payload?: number) => ({ diff --git a/packages/toolkit/src/tests/createAsyncThunk.test.ts b/packages/toolkit/src/tests/createAsyncThunk.test.ts index 8208d8693e..fbc2b8a74a 100644 --- a/packages/toolkit/src/tests/createAsyncThunk.test.ts +++ b/packages/toolkit/src/tests/createAsyncThunk.test.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { createAsyncThunk, unwrapResult, @@ -377,14 +377,14 @@ describe('createAsyncThunk with abortController', () => { ) let store = configureStore({ - reducer(store: AnyAction[] = []) { + reducer(store: UnknownAction[] = []) { return store }, }) beforeEach(() => { store = configureStore({ - reducer(store: AnyAction[] = [], action) { + reducer(store: UnknownAction[] = [], action) { return [...store, action] }, }) diff --git a/packages/toolkit/src/tests/createAsyncThunk.typetest.ts b/packages/toolkit/src/tests/createAsyncThunk.typetest.ts index 2bfc8c058e..e5e90a8e40 100644 --- a/packages/toolkit/src/tests/createAsyncThunk.typetest.ts +++ b/packages/toolkit/src/tests/createAsyncThunk.typetest.ts @@ -1,5 +1,9 @@ /* eslint-disable no-lone-blocks */ -import type { AnyAction, SerializedError, AsyncThunk } from '@reduxjs/toolkit' +import type { + UnknownAction, + SerializedError, + AsyncThunk, +} from '@reduxjs/toolkit' import { createAsyncThunk, createReducer, @@ -19,8 +23,8 @@ import type { } from '@internal/createAsyncThunk' const ANY = {} as any -const defaultDispatch = (() => {}) as ThunkDispatch<{}, any, AnyAction> -const anyAction = { type: 'foo' } as AnyAction +const defaultDispatch = (() => {}) as ThunkDispatch<{}, any, UnknownAction> +const unknownAction = { type: 'foo' } as UnknownAction // basic usage ;(async function () { @@ -90,7 +94,7 @@ const anyAction = { type: 'foo' } as AnyAction const correctDispatch = (() => {}) as ThunkDispatch< BookModel[], { userAPI: Function }, - AnyAction + UnknownAction > // Verify that the the first type args to createAsyncThunk line up right @@ -489,8 +493,8 @@ const anyAction = { type: 'foo' } as AnyAction serializeError: funkySerializeError, }) - if (shouldWork.rejected.match(anyAction)) { - expectType(anyAction.error) + if (shouldWork.rejected.match(unknownAction)) { + expectType(unknownAction.error) } } @@ -645,7 +649,7 @@ const anyAction = { type: 'foo' } as AnyAction // correct dispatch type const test2: number = api.dispatch((dispatch, getState) => { expectExactType< - ThunkDispatch<{ foo: { value: number } }, undefined, AnyAction> + ThunkDispatch<{ foo: { value: number } }, undefined, UnknownAction> >(ANY)(dispatch) expectExactType<() => { foo: { value: number } }>(ANY)(getState) return getState().foo.value @@ -671,7 +675,7 @@ const anyAction = { type: 'foo' } as AnyAction // correct dispatch type const test2: number = api.dispatch((dispatch, getState) => { expectExactType< - ThunkDispatch<{ foo: { value: number } }, undefined, AnyAction> + ThunkDispatch<{ foo: { value: number } }, undefined, UnknownAction> >(ANY)(dispatch) expectExactType<() => { foo: { value: number } }>(ANY)(getState) return getState().foo.value @@ -698,7 +702,7 @@ const anyAction = { type: 'foo' } as AnyAction // correct dispatch type const test2: number = api.dispatch((dispatch, getState) => { expectExactType< - ThunkDispatch<{ foo: { value: number } }, undefined, AnyAction> + ThunkDispatch<{ foo: { value: number } }, undefined, UnknownAction> >(ANY)(dispatch) expectExactType<() => { foo: { value: number } }>(ANY)(getState) return getState().foo.value diff --git a/packages/toolkit/src/tests/createReducer.test.ts b/packages/toolkit/src/tests/createReducer.test.ts index ee845e3b3a..88615b45a6 100644 --- a/packages/toolkit/src/tests/createReducer.test.ts +++ b/packages/toolkit/src/tests/createReducer.test.ts @@ -4,8 +4,9 @@ import type { PayloadAction, Draft, Reducer, - AnyAction, + UnknownAction, } from '@reduxjs/toolkit' +import { isPlainObject } from '@reduxjs/toolkit' import { createReducer, createAction, createNextState } from '@reduxjs/toolkit' import { mockConsole, @@ -378,10 +379,19 @@ describe('createReducer', () => { meta: { type: 'string_action' }, }) - const numberActionMatcher = (a: AnyAction): a is PayloadAction => - a.meta && a.meta.type === 'number_action' - const stringActionMatcher = (a: AnyAction): a is PayloadAction => - a.meta && a.meta.type === 'string_action' + const numberActionMatcher = ( + a: UnknownAction + ): a is PayloadAction => + isPlainObject(a.meta) && + 'type' in a.meta && + (a.meta as Record<'type', unknown>).type === 'number_action' + + const stringActionMatcher = ( + a: UnknownAction + ): a is PayloadAction => + isPlainObject(a.meta) && + 'type' in a.meta && + (a.meta as Record<'type', unknown>).type === 'string_action' const incrementBy = createAction('increment', prepareNumberAction) const decrementBy = createAction('decrement', prepareNumberAction) diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts index 9a343bd2fb..a3e62dc7cc 100644 --- a/packages/toolkit/src/tests/createSlice.test.ts +++ b/packages/toolkit/src/tests/createSlice.test.ts @@ -275,7 +275,10 @@ describe('createSlice', () => { initialState: 0, reducers: {}, extraReducers: (builder) => - builder.addDefaultCase((state, action) => state + action.payload), + builder.addDefaultCase( + (state, action) => + state + (action as PayloadAction).payload + ), }) expect(slice.reducer(0, increment(5))).toBe(5) }) diff --git a/packages/toolkit/src/tests/createSlice.typetest.ts b/packages/toolkit/src/tests/createSlice.typetest.ts index 0636d236b6..f7e535649d 100644 --- a/packages/toolkit/src/tests/createSlice.typetest.ts +++ b/packages/toolkit/src/tests/createSlice.typetest.ts @@ -1,4 +1,4 @@ -import type { Action, AnyAction, Reducer } from 'redux' +import type { Action, UnknownAction, Reducer } from 'redux' import type { ActionCreatorWithNonInferrablePayload, ActionCreatorWithOptionalPayload, @@ -82,7 +82,7 @@ const value = actionCreators.anyKey // @ts-expect-error expectType>(slice.reducer) // @ts-expect-error - expectType>(slice.reducer) + expectType>(slice.reducer) /* Actions */ slice.actions.increment(1) diff --git a/packages/toolkit/src/tests/getDefaultMiddleware.test.ts b/packages/toolkit/src/tests/getDefaultMiddleware.test.ts index 74dcbf5874..36c55fcfb6 100644 --- a/packages/toolkit/src/tests/getDefaultMiddleware.test.ts +++ b/packages/toolkit/src/tests/getDefaultMiddleware.test.ts @@ -1,6 +1,6 @@ import { vi } from 'vitest' import type { - AnyAction, + UnknownAction, Middleware, ThunkAction, Action, @@ -94,10 +94,14 @@ describe('getDefaultMiddleware', () => { const dummyMiddleware2: Middleware<{}, { counter: number }> = (storeApi) => (next) => (action) => {} - const testThunk: ThunkAction = - (dispatch, getState, extraArg) => { - expect(extraArg).toBe(extraArgument) - } + const testThunk: ThunkAction< + void, + { counter: number }, + number, + UnknownAction + > = (dispatch, getState, extraArg) => { + expect(extraArg).toBe(extraArgument) + } const reducer = () => ({ counter: 123 }) @@ -116,15 +120,15 @@ describe('getDefaultMiddleware', () => { expectType< Tuple< [ - ThunkMiddleware, + ThunkMiddleware, Middleware< (action: Action<'actionListenerMiddleware/add'>) => () => void, { counter: number }, - Dispatch + Dispatch >, - Middleware<{}, any, Dispatch> + Middleware<{}, any, Dispatch> ] > >(m3) @@ -133,7 +137,7 @@ describe('getDefaultMiddleware', () => { }, }) - expectType & Dispatch>( + expectType & Dispatch>( store.dispatch ) diff --git a/packages/toolkit/src/tests/mapBuilders.typetest.ts b/packages/toolkit/src/tests/mapBuilders.typetest.ts index 42bfdd5a05..334572300e 100644 --- a/packages/toolkit/src/tests/mapBuilders.typetest.ts +++ b/packages/toolkit/src/tests/mapBuilders.typetest.ts @@ -1,7 +1,7 @@ import type { SerializedError } from '@internal/createAsyncThunk' import { createAsyncThunk } from '@internal/createAsyncThunk' import { executeReducerBuilderCallback } from '@internal/mapBuilders' -import type { AnyAction } from '@reduxjs/toolkit' +import type { UnknownAction } from '@reduxjs/toolkit' import { createAction } from '@reduxjs/toolkit' import { expectExactType, expectType } from './helpers' @@ -66,16 +66,16 @@ import { expectExactType, expectType } from './helpers' (action): action is PredicateWithoutTypeProperty => true, (state, action) => { expectType(action) - expectType(action) + expectType(action) } ) } - // action type defaults to AnyAction if no type predicate matcher is passed + // action type defaults to UnknownAction if no type predicate matcher is passed builder.addMatcher( () => true, (state, action) => { - expectExactType({} as AnyAction)(action) + expectExactType({} as UnknownAction)(action) } ) @@ -84,7 +84,7 @@ import { expectExactType, expectType } from './helpers' () => true, (state, action) => { expectType<{ foo: boolean }>(action) - expectType(action) + expectType(action) } ) @@ -98,14 +98,14 @@ import { expectExactType, expectType } from './helpers' expectType>(action) }) - // addCase().addDefaultCase() is possible, action type is AnyAction + // addCase().addDefaultCase() is possible, action type is UnknownAction builder .addCase( 'increment', (state, action: ReturnType) => state ) .addDefaultCase((state, action) => { - expectType(action) + expectType(action) }) { diff --git a/packages/toolkit/src/tests/matchers.test.ts b/packages/toolkit/src/tests/matchers.test.ts index 7282c5fa65..54dbbed37e 100644 --- a/packages/toolkit/src/tests/matchers.test.ts +++ b/packages/toolkit/src/tests/matchers.test.ts @@ -1,5 +1,5 @@ import { vi } from 'vitest' -import type { ThunkAction, AnyAction } from '@reduxjs/toolkit' +import type { ThunkAction, UnknownAction } from '@reduxjs/toolkit' import { isAllOf, isAnyOf, @@ -13,7 +13,7 @@ import { createReducer, } from '@reduxjs/toolkit' -const thunk: ThunkAction = () => {} +const thunk: ThunkAction = () => {} describe('isAnyOf', () => { it('returns true only if any matchers match (match function)', () => { diff --git a/packages/toolkit/src/tests/matchers.typetest.ts b/packages/toolkit/src/tests/matchers.typetest.ts index be82806de5..495796727f 100644 --- a/packages/toolkit/src/tests/matchers.typetest.ts +++ b/packages/toolkit/src/tests/matchers.typetest.ts @@ -1,5 +1,5 @@ import { expectExactType, expectUnknown } from './helpers' -import type { AnyAction } from 'redux' +import type { UnknownAction } from 'redux' import type { SerializedError } from '../../src' import { createAction, @@ -18,7 +18,7 @@ import { /* * Test: isAnyOf correctly narrows types when used with action creators */ -function isAnyOfActionTest(action: AnyAction) { +function isAnyOfActionTest(action: UnknownAction) { const actionA = createAction('a', () => { return { payload: { @@ -51,7 +51,7 @@ function isAnyOfActionTest(action: AnyAction) { /* * Test: isAnyOf correctly narrows types when used with async thunks */ -function isAnyOfThunkTest(action: AnyAction) { +function isAnyOfThunkTest(action: UnknownAction) { const asyncThunk1 = createAsyncThunk<{ prop1: number; prop3: number }>( 'asyncThunk1', @@ -88,7 +88,7 @@ function isAnyOfThunkTest(action: AnyAction) { /* * Test: isAnyOf correctly narrows types when used with type guards */ -function isAnyOfTypeGuardTest(action: AnyAction) { +function isAnyOfTypeGuardTest(action: UnknownAction) { interface ActionA { type: 'a' payload: { @@ -140,7 +140,7 @@ const isSpecialAction = (v: any): v is SpecialAction => { * Test: isAllOf correctly narrows types when used with action creators * and type guards */ -function isAllOfActionTest(action: AnyAction) { +function isAllOfActionTest(action: UnknownAction) { const actionA = createAction('a', () => { return { payload: { @@ -165,7 +165,7 @@ function isAllOfActionTest(action: AnyAction) { * Test: isAllOf correctly narrows types when used with async thunks * and type guards */ -function isAllOfThunkTest(action: AnyAction) { +function isAllOfThunkTest(action: UnknownAction) { const asyncThunk1 = createAsyncThunk<{ prop1: number; prop3: number }>( 'asyncThunk1', @@ -191,7 +191,7 @@ function isAllOfThunkTest(action: AnyAction) { /* * Test: isAnyOf correctly narrows types when used with type guards */ -function isAllOfTypeGuardTest(action: AnyAction) { +function isAllOfTypeGuardTest(action: UnknownAction) { interface ActionA { type: 'a' payload: { @@ -218,7 +218,7 @@ function isAllOfTypeGuardTest(action: AnyAction) { /* * Test: isPending correctly narrows types */ -function isPendingTest(action: AnyAction) { +function isPendingTest(action: UnknownAction) { if (isPending(action)) { expectExactType(action.payload) // @ts-expect-error @@ -237,7 +237,7 @@ function isPendingTest(action: AnyAction) { /* * Test: isRejected correctly narrows types */ -function isRejectedTest(action: AnyAction) { +function isRejectedTest(action: UnknownAction) { if (isRejected(action)) { // might be there if rejected with payload expectUnknown(action.payload) @@ -256,7 +256,7 @@ function isRejectedTest(action: AnyAction) { /* * Test: isFulfilled correctly narrows types */ -function isFulfilledTest(action: AnyAction) { +function isFulfilledTest(action: UnknownAction) { if (isFulfilled(action)) { expectUnknown(action.payload) // @ts-expect-error @@ -274,7 +274,7 @@ function isFulfilledTest(action: AnyAction) { /* * Test: isAsyncThunkAction correctly narrows types */ -function isAsyncThunkActionTest(action: AnyAction) { +function isAsyncThunkActionTest(action: UnknownAction) { if (isAsyncThunkAction(action)) { expectUnknown(action.payload) // do not expect an error property because pending/fulfilled lack it @@ -295,7 +295,7 @@ function isAsyncThunkActionTest(action: AnyAction) { /* * Test: isRejectedWithValue correctly narrows types */ -function isRejectedWithValueTest(action: AnyAction) { +function isRejectedWithValueTest(action: UnknownAction) { if (isRejectedWithValue(action)) { expectUnknown(action.payload) expectExactType(action.error) diff --git a/packages/toolkit/src/tests/serializableStateInvariantMiddleware.test.ts b/packages/toolkit/src/tests/serializableStateInvariantMiddleware.test.ts index 24c00cacd9..2d28f66330 100644 --- a/packages/toolkit/src/tests/serializableStateInvariantMiddleware.test.ts +++ b/packages/toolkit/src/tests/serializableStateInvariantMiddleware.test.ts @@ -3,7 +3,7 @@ import { createConsole, getLog, } from 'console-testing-library/pure' -import type { AnyAction, Reducer } from '@reduxjs/toolkit' +import type { Reducer } from '@reduxjs/toolkit' import { createNextState, configureStore, diff --git a/yarn.lock b/yarn.lock index 054ecbd38a..d5208aed54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6784,7 +6784,7 @@ __metadata: node-fetch: ^2.6.1 prettier: ^2.2.1 query-string: ^7.0.1 - redux: 5.0.0-alpha.6 + redux: 5.0.0-beta.0 redux-thunk: 3.0.0-alpha.3 reselect: ^5.0.0-alpha.2 rimraf: ^3.0.2 @@ -24669,10 +24669,10 @@ fsevents@^1.2.7: languageName: node linkType: hard -"redux@npm:5.0.0-alpha.6": - version: 5.0.0-alpha.6 - resolution: "redux@npm:5.0.0-alpha.6" - checksum: 6f95a75f4c2549dc891700af0aba146fa3f2983bb07918ff47c5635dfdf3b15033d5d1e9981183b291bd70edd59a5842cbc54c6c47d57697c8195f3aef1ffb84 +"redux@npm:5.0.0-beta.0": + version: 5.0.0-beta.0 + resolution: "redux@npm:5.0.0-beta.0" + checksum: 11df373e219f2f515ee1bda1a19a1ba5de02d8d5c874800ec353179dcd106eddd54432946fd0ab37c47f99f8fe53f820a6404c14da7f039a46022187e9469d2d languageName: node linkType: hard