Redux Observable带Payload的Action类型不匹配,无Payload可正常运行求助
I've run into this exact type of issue before with Redux Observable and TypeScript—those union type inference quirks can be tricky! Let's break down what's going wrong and how to fix it.
The Root of the Problem
Your current code has three main type-related pain points:
- Over-specifying the
ofTypeoperator's generics in a way that confuses TypeScript's union type narrowing. - Using
anyin themapoperator, which bypasses type checking and hides underlying mismatches. - Missing explicit type parameters for
createEpicMiddleware, which helps TypeScript enforce consistency across your store and epics.
Step-by-Step Fixes
1. Simplify ofType Usage
Redux Observable's ofType is designed to automatically narrow union action types when you pass the action type string. You don't need to manually specify the generics—let TypeScript do the work for you.
Replace this:
ofType<Actions, One>(ActionTypes.One), map((action: any) => ({ type: ActionTypes.Two, myBool: true }))
With this:
ofType(ActionTypes.One), map((action) => { // TypeScript now knows `action` is of type `One`—you can safely access `action.myStr` here! console.log("Received string payload:", action.myStr); return doTwo(true); // Use your action creator for guaranteed type safety })
Using your existing doTwo action creator ensures the output action matches the Two interface perfectly, no manual object creation or type guesswork needed.
2. Add Explicit Types to createEpicMiddleware
To keep your store and epics aligned, specify the action and state types when creating the epic middleware. First, define your root state type (adjust this to match your actual reducer's state):
In epic.ts:
// Add this interface based on your reducer's actual state structure export interface RootState { // Example state fields—replace with your real state properties someTrackedValue: string; }
Then update your store config:
import { createEpicMiddleware } from "redux-observable" import { createStore, applyMiddleware } from "redux" import { reducer } from "./reducer" import { rootEpic, Actions, RootState } from "./epic" export const configStore = () => { // Explicitly define action and state types for the middleware const epicMiddleware = createEpicMiddleware<Actions, Actions, RootState>() // Also type your store to enforce state and action consistency const store = createStore<RootState, Actions, unknown, unknown>( reducer, applyMiddleware(epicMiddleware) ) epicMiddleware.run(rootEpic) return store }
3. Verify Your Action Interfaces
Your existing One and Two interfaces are correct (they extend Redux's Action and explicitly set the type field), so no changes needed here—but double-check that your action creators (doOne, doTwo) strictly match these interfaces (which they do in your code).
Full Updated Code
epic.ts
import { Action } from "redux"; import { ActionsObservable, ofType, combineEpics } from 'redux-observable' import { map } from 'rxjs/operators' export enum ActionTypes { One = 'ACTION_ONE', Two = 'ACTION_TWO' } export interface One extends Action { type: ActionTypes.One myStr: string } export const doOne = (myStr: string): One => ({ type: ActionTypes.One, myStr }) export interface Two extends Action { type: ActionTypes.Two myBool: boolean } export const doTwo = (myBool: boolean): Two => ({ type: ActionTypes.Two, myBool }) export type Actions = One | Two // Define your root state type here export interface RootState { someTrackedValue: string; } export const epic = (action$: ActionsObservable<Actions>) => action$.pipe( ofType(ActionTypes.One), map((action) => { console.log("Processing action with payload:", action.myStr); return doTwo(true); }) ) export const rootEpic = combineEpics(epic)
store.ts
import { createEpicMiddleware } from "redux-observable" import { createStore, applyMiddleware } from "redux" import { reducer } from "./reducer" import { rootEpic, Actions, RootState } from "./epic" export const configStore = () => { const epicMiddleware = createEpicMiddleware<Actions, Actions, RootState>() const store = createStore<RootState, Actions, unknown, unknown>( reducer, applyMiddleware(epicMiddleware) ) epicMiddleware.run(rootEpic) return store }
Why This Works
- By removing manual generics from
ofType, TypeScript can properly narrow the unionActionstype to justOnewhen it sees theActionTypes.Onetype string. - Using action creators ensures your output actions always match their interface definitions, eliminating type mismatches.
- Explicitly typing
createEpicMiddlewareandcreateStorecreates a single source of truth for your action and state types, preventing subtle type bugs.
If you're still running into issues, make sure you're on the latest version of Redux Observable—older versions had some type inference bugs that have been fixed in recent releases.
内容的提问来源于stack exchange,提问作者Apoorv Mote

