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

如何在Redux中无需显式转换实现TypeScript枚举类型收窄?

好问题!你现在的写法确实有点繁琐,我们可以通过TypeScript的类型推断特性来去掉那些重复的显式类型转换,同时保持Reducer里的类型安全。下面有几种不同的实现方式,你可以根据自己的偏好选择:

方法1:改用函数式Action Creators(推荐)

如果不需要用类来组织Action creators,这种方式最简洁,完全不需要类型断言:

export enum ActionTypes {
  INCREMENT_BY_ONE = 'INCREMENT_BY_ONE',
  INCREMENT_BY_MULTIPLE = 'INCREMENT_BY_MULTIPLE',
}

// 每个Action creator都是独立函数,TypeScript自动推断返回类型
export const incrementByOne = () => ({
  type: ActionTypes.INCREMENT_BY_ONE,
} as const); // 用as const强制推断为字面量类型

export const incrementByMultiple = (multiple: number) => ({
  type: ActionTypes.INCREMENT_BY_MULTIPLE,
  payload: multiple,
} as const);

// 自动生成所有Action的联合类型
type ActionCreators = typeof incrementByOne | typeof incrementByMultiple;
export type KnownAction = ReturnType<ActionCreators>;

function reducer(state: number = 0, action: KnownAction) {
  switch(action.type) {
    case ActionTypes.INCREMENT_BY_ONE:
      // TypeScript会正确识别这里没有payload,尝试访问会报错(符合预期)
      // const a = action.payload; ❌
      return state + 1;
    case ActionTypes.INCREMENT_BY_MULTIPLE:
      // payload自动推断为number类型
      return state * action.payload; ✅
    default:
      return state;
  }
}

为什么这能行?
as const告诉TypeScript把对象的属性推断为字面量类型(比如ActionTypes.INCREMENT_BY_ONE而不是宽泛的ActionTypes),这样TypeScript就能在switch case中准确收窄Action的类型,自动识别每个case对应的payload是否存在及其类型。

方法2:保留类结构,用as const简化

如果你想继续用类来组织Action creators,只需要在每个方法的返回值后加上as const,就能去掉显式类型转换:

export enum ActionTypes {
  INCREMENT_BY_ONE = 'INCREMENT_BY_ONE',
  INCREMENT_BY_MULTIPLE = 'INCREMENT_BY_MULTIPLE',
}

class Actions {
  incrementByOne() {
    return { type: ActionTypes.INCREMENT_BY_ONE } as const;
  }
  incrementByMultiple(multiple: number) {
    return { type: ActionTypes.INCREMENT_BY_MULTIPLE, payload: multiple } as const;
  }
}

type ValueOf<T> = T[keyof T];
export type KnownAction = ReturnType<ValueOf<Actions>>;

function reducer(state: number = 0, action: KnownAction) {
  switch(action.type) {
    case ActionTypes.INCREMENT_BY_ONE:
      // 正确识别无payload
      return state + 1;
    case ActionTypes.INCREMENT_BY_MULTIPLE:
      // payload类型为number
      return state * action.payload;
    default:
      return state;
  }
}

这种方式和你原来的类结构兼容,同时避免了重复的类型断言代码。

方法3:显式指定方法返回类型(更严谨但稍繁琐)

如果你不想用as const,也可以手动给每个类方法指定返回类型,同样能达到类型安全的效果:

export enum ActionTypes {
  INCREMENT_BY_ONE = 'INCREMENT_BY_ONE',
  INCREMENT_BY_MULTIPLE = 'INCREMENT_BY_MULTIPLE',
}

class Actions {
  incrementByOne(): { type: ActionTypes.INCREMENT_BY_ONE } {
    return { type: ActionTypes.INCREMENT_BY_ONE };
  }
  incrementByMultiple(multiple: number): { type: ActionTypes.INCREMENT_BY_MULTIPLE; payload: number } {
    return { type: ActionTypes.INCREMENT_BY_MULTIPLE, payload: multiple };
  }
}

type ValueOf<T> = T[keyof T];
export type KnownAction = ReturnType<ValueOf<Actions>>;

function reducer(state: number = 0, action: KnownAction) {
  switch(action.type) {
    case ActionTypes.INCREMENT_BY_ONE:
      return state + 1;
    case ActionTypes.INCREMENT_BY_MULTIPLE:
      return state * action.payload;
    default:
      return state;
  }
}

这种方式需要手动定义每个方法的返回类型,虽然更繁琐,但类型定义更清晰,适合对类型严谨性要求更高的场景。

总结

  • 优先推荐方法1:函数式Action creators + as const,最简洁且类型安全。
  • 如果必须保留类结构,方法2是最优选择,用as const简化代码。
  • 方法3适合需要显式类型定义的场景,但稍显繁琐。

所有这些方法都能让你去掉那些重复的<ActionTypes.X>ActionTypes.X类型断言,同时保持Reducer中Action.payload的类型正确识别。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:09:32