You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Reducer中使用区分联合类型遇类型错误,求排查方案

问题根源

你的错误来自Reducer返回的对象不符合SampleState联合类型的约束。TypeScript的区分联合类型要求每个成员的结构严格匹配,你的代码存在两个关键问题:

  1. SET_SAMPLE_LOADING_STATE Action设计不合理:它允许传入任意LoadingState值,但当传入loaded时,返回的对象缺少sampleDetails字段(原状态如果是idle/loading/error类型,本身没有该字段),无法匹配SampleState中loaded类型的要求;同理传入error时缺少error字段。
  2. 使用...state扩展原状态的方式不安全:原状态的结构和目标状态可能不兼容,比如从loaded状态切换到idle时,...state会保留sampleDetails字段,导致返回的对象包含多余字段,不符合idle类型的定义。
修复方案

方案1:拆分Action,明确状态转换

将通用的SET_SAMPLE_LOADING_STATE拆分为多个针对性的Action,确保每个Action只对应一种状态转换,让TypeScript能准确推断返回类型:

export const enum LoadingState {
  idle = "idle",
  loading = "loading",
  loaded = "loaded",
  error = "error",
}

export type SampleState =
  | { status: LoadingState.idle }
  | { status: LoadingState.loading }
  | { status: LoadingState.loaded; sampleDetails: SampleDetails }
  | { status: LoadingState.error; error: any };

export const INITIAL_STATE: SampleState = { status: LoadingState.idle };

// 拆分后的Action类型
export type Action =
  | { type: "SET_IDLE" }
  | { type: "SET_LOADING" }
  | { type: "SET_ERROR"; payload: { error: any } }
  | { type: "SET_SAMPLE"; payload: { sampleDetails: SampleDetails } };

export const reducer = (state: SampleState, action: Action): SampleState => {
  switch (action.type) {
    case "SET_IDLE":
      return { status: LoadingState.idle };
    case "SET_LOADING":
      return { status: LoadingState.loading };
    case "SET_ERROR":
      return { status: LoadingState.error, error: action.payload.error };
    case "SET_SAMPLE":
      return {
        status: LoadingState.loaded,
        sampleDetails: action.payload.sampleDetails,
      };
    default:
      return state;
  }
};

// 使用示例
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);

方案2:保留原Action,添加类型守卫

如果想保留SET_SAMPLE_LOADING_STATE,需要在Reducer中通过类型守卫确保返回对象符合对应状态的结构,同时排除不合理的状态转换:

export const reducer = (state: SampleState, action: Action): SampleState => {
  switch (action.type) {
    case "SET_SAMPLE_LOADING_STATE":
      const { status } = action.payload;
      // 针对不同status返回对应完整结构,排除loaded(由SET_SAMPLE处理)
      switch (status) {
        case LoadingState.idle:
          return { status };
        case LoadingState.loading:
          return { status };
        case LoadingState.error:
          return { status, error: state.error ?? "Unknown error" };
        case LoadingState.loaded:
          // 禁止通过该Action切换到loaded,直接返回原状态
          return state;
      }
    case "SET_SAMPLE":
      return {
        status: LoadingState.loaded,
        sampleDetails: action.payload.sampleDetails,
      };
    default:
      return state;
  }
};
关键注意点
  • 区分联合类型的核心是每个成员结构严格唯一,不能有重叠或缺失字段的情况。
  • Reducer的返回值必须严格匹配SampleState的某个成员,不能返回混合多个成员字段的对象。
  • 尽量让每个Action对应单一状态转换,既保证类型安全,也提升代码可读性。

内容的提问来源于stack exchange,提问作者Nicole Staline

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 13:35:27