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

如何在typesafe-actions的createAsyncAction中收窄TypeScript Action类型

解决typesafe-actions异步Action的类型收窄与样板代码问题

我来帮你搞定这个TypeScript类型收窄的问题,同时帮你砍掉那些繁琐的样板代码,让代码更简洁易维护。

第一步:简化AuthActions类型定义,告别重复代码

你之前手动联合AuthRequestActions/AuthSuccessActions/AuthErrorActions的方式太繁琐了,完全可以利用typesafe-actions的ActionType工具自动生成完整的Action联合类型。

修改你的auth.actions.ts:

import { ActionType, createAsyncAction } from 'typesafe-actions';
import {
  getAsyncFetchActionConstants,
  getAsyncCreateActionConstants,
  getAsyncDeleteActionConstants,
} from '../utils/asyncActions.utils';

const BASE_ACTION = 'AUTH';

// 定义所有异步Action
export const fetchAuth = createAsyncAction(
  ...getAsyncFetchActionConstants(BASE_ACTION))<void, AuthState, Error>();
export const createAuth = createAsyncAction(
  ...getAsyncCreateActionConstants(BASE_ACTION))<void, AuthState, Error>();
export const deleteAuth = createAsyncAction(
  ...getAsyncDeleteActionConstants(BASE_ACTION))<void, AuthState, Error>();

// 把所有Action打包成一个对象
const authActions = {
  fetchAuth,
  createAuth,
  deleteAuth,
};

// 自动生成包含所有request/success/failure状态的Action联合类型
export type AuthActions = ActionType<typeof authActions>;

这样一来,你再也不用手动编写那一堆重复的类型联合了,ActionType会自动识别每个异步Action的三个状态,并把它们全部合并到AuthActions类型里。

第二步:修复Reducer的类型收窄问题

现在回到你的Reducer里,之前的类型错误是因为TS无法正确识别当前case对应的Action类型。现在用简化后的AuthActions类型,配合getType就能完美解决类型收窄的问题:

修改你的auth.reducer.ts:

import { getType } from 'typesafe-actions';
import { AUTH_INITIAL_STATE } from './auth.constants';
import { fetchAuth, createAuth, deleteAuth, AuthActions } from './auth.actions';

export function authReducer(state: AuthState = AUTH_INITIAL_STATE, action: AuthActions) {
  switch (action.type) {
    case getType(fetchAuth.success):
      // 现在TS会自动把action收窄为SuccessAction类型,payload可以直接访问
      return { ...state, ...action.payload };
    case getType(createAuth.success):
      return { ...state, ...action.payload };
    case getType(deleteAuth.success):
      return AUTH_INITIAL_STATE;
    // 可以轻松添加request和failure的处理逻辑,类型同样会自动收窄
    case getType(fetchAuth.request):
      return { ...state, loading: true, error: null };
    case getType(fetchAuth.failure):
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
}

为什么这样能解决问题?

ActionType<typeof authActions>生成的联合类型包含了每个异步Action三个状态的完整类型信息,当你用getType(fetchAuth.success)匹配action.type时,TS会精准识别当前action的类型,自动排除掉没有payload的request状态Action,所以再也不会出现Property 'payload' does not exist的错误。

额外优化:统一异步常量生成逻辑(可选)

如果你的getAsyncFetchActionConstants这类函数逻辑很相似,还可以进一步简化常量生成的代码,比如写一个通用的CRUD常量生成函数:

// asyncActions.utils.ts
const ASYNC_REQUEST = 'REQUEST';
const ASYNC_SUCCESS = 'SUCCESS';
const ASYNC_FAILURE = 'FAILURE';

export const getCrudAsyncConstants = (baseAction: string) => ({
  fetch: [`${baseAction}_FETCH_${ASYNC_REQUEST}`, `${baseAction}_FETCH_${ASYNC_SUCCESS}`, `${baseAction}_FETCH_${ASYNC_FAILURE}`],
  create: [`${baseAction}_CREATE_${ASYNC_REQUEST}`, `${baseAction}_CREATE_${ASYNC_SUCCESS}`, `${baseAction}_CREATE_${ASYNC_FAILURE}`],
  delete: [`${baseAction}_DELETE_${ASYNC_REQUEST}`, `${baseAction}_DELETE_${ASYNC_SUCCESS}`, `${baseAction}_DELETE_${ASYNC_FAILURE}`],
});

// 在auth.actions.ts中使用
const authConstants = getCrudAsyncConstants(BASE_ACTION);
export const fetchAuth = createAsyncAction(...authConstants.fetch)<void, AuthState, Error>();

这样可以进一步减少重复的常量拼接代码,让项目结构更整洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:15:23