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

TypeScript 2.8:简化类型安全Reducer遇信息丢失问题求助

解决Redux Action类型信息丢失问题:结合手动接口与ReturnType推导

我懂你遇到的痛点——尝试用ReturnType<A[keyof A]>简化Redux Reducer的类型定义时,手动创建的Action接口(比如MyFirstAction、MySecondAction)总是会丢失属性信息,导致Reducer里没法正确识别Action的额外字段对吧?这其实是因为手动接口和action creator的返回类型没有建立强关联,让TypeScript没法正确映射两者的关系。下面给你两种可行的解决方案:

方案一:让Action Creator严格关联手动定义的接口

如果你还是想保留手动定义的Action接口(比如需要在其他地方复用这些接口),只需要确保每个action creator的返回类型明确指向对应的接口,而不是让TypeScript自动推导:

import { Action } from 'redux';

// 枚举Action类型
enum MyEnum {
  FirstAction = 'FIRST_ACTION',
  SecondAction = 'SECOND_ACTION'
}

// 手动定义Action接口
export interface MyFirstAction extends Action<MyEnum.FirstAction> {
  someProperty: string;
}

export interface MySecondAction extends Action<MyEnum.SecondAction> {
  numProperty: number;
}

// 让action creator的返回类型严格对应接口
const myFirstAction = (payload: string): MyFirstAction => ({
  type: MyEnum.FirstAction,
  someProperty: payload
});

const mySecondAction = (payload: number): MySecondAction => ({
  type: MyEnum.SecondAction,
  numProperty: payload
});

// 把所有action creator整合到一个对象
const actionCreators = {
  myFirstAction,
  mySecondAction
};

// 推导完整的Action类型集合
type AppActions = ReturnType<typeof actionCreators[keyof typeof actionCreators]>;

这样AppActions就会准确包含MyFirstAction和MySecondAction的所有类型信息,Reducer里使用这个类型时,每个case分支的Action属性都会被正确识别。

方案二:完全通过Action Creator推导Action类型(更简洁)

如果不需要单独复用Action接口,推荐直接通过action creator的返回值来推导所有Action类型,这样从根源上避免信息丢失,代码也更简洁:

import { Action } from 'redux';

enum MyEnum {
  FirstAction = 'FIRST_ACTION',
  SecondAction = 'SECOND_ACTION'
}

// 直接定义action creators,不用手动写接口
const myFirstAction = (someProperty: string) => ({
  type: MyEnum.FirstAction,
  someProperty
});

const mySecondAction = (numProperty: number) => ({
  type: MyEnum.SecondAction,
  numProperty
});

const actionCreators = {
  myFirstAction,
  mySecondAction
};

// 自动推导所有Action类型
type AppActions = ReturnType<typeof actionCreators[keyof typeof actionCreators]>;

在Reducer中使用推导后的类型

不管用哪种方案,Reducer里的类型都会完美工作:

type AppState = {
  someValue?: string;
  numValue?: number;
};

const initialState: AppState = {};

const reducer = (state: AppState = initialState, action: AppActions): AppState => {
  switch (action.type) {
    case MyEnum.FirstAction:
      // 这里action.someProperty会被正确推断为string类型
      return { ...state, someValue: action.someProperty };
    case MyEnum.SecondAction:
      // action.numProperty会被正确推断为number类型
      return { ...state, numValue: action.numProperty };
    default:
      return state;
  }
};

关键注意点

  • 如果你坚持手动定义Action接口,不要省略action creator的返回类型标注——让TypeScript自动推导返回类型的话,可能会和手动接口产生细微差异,导致类型信息丢失。
  • 用typeof actionCreators[keyof typeof actionCreators]遍历所有action creator,确保不会漏掉任何一个Action类型,也避免了手动维护Action联合类型的麻烦。

内容的提问来源于stack exchange,提问作者Erik Živković

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:06:38