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

Redux Toolkit Async Thunk提取重复try-catch代码的TypeScript问题解决

提取Redux Toolkit Async Thunk通用try-catch逻辑的TypeScript问题解决

问题背景

我在Redux Toolkit的features目录下的slice文件中定义了多个Async Thunk函数,每个函数都包含重复的try-catch错误处理逻辑,希望将这部分逻辑提取为独立函数以消除代码冗余。当前代码可正常运行,getPostComments、createComment、deleteComment均为axios请求函数。

现有重复代码

export const fetchComments = createAsyncThunk('users/fetchComments', async (id: number, thunkAPI) => {
  try {
    return await getPostComments(id);
  } catch (err) {
    if (err instanceof Error) {
      return thunkAPI.rejectWithValue(err.message);
    }

    return thunkAPI.rejectWithValue('something went wrong');
  }
});

export const addComment = createAsyncThunk(
  'users/addComment', async (data: CommentData, thunkAPI) => {
    try {
      return await createComment(data);
    } catch (err) {
      if (err instanceof Error) {
        return thunkAPI.rejectWithValue(err.message);
      }

      return thunkAPI.rejectWithValue('something went wrong');
    }
  },
);

export const removeComment = createAsyncThunk(
  'users/removeComment', async (id: number, thunkAPI) => {
    try {
      await deleteComment(id);

      return id;
    } catch (err) {
      if (err instanceof Error) {
        return thunkAPI.rejectWithValue(err.message);
      }

      return thunkAPI.rejectWithValue('something went wrong');
    }
  },
);

尝试的通用函数及遇到的错误

我尝试编写通用try-catch函数时遇到了TypeScript错误,比如TS2304: Cannot find name 'GetThunkAPI',同时还出现了以下问题:

  • 无法识别GetThunkAPI类型
  • Async Thunk的回调函数返回类型不匹配(未返回通用函数的执行结果)

尝试的通用函数代码:

const tryCatchMiddleware = async (
  callback: (arg: number) => Promise<Comment[]>,
  thunkAPI: GetThunkAPI<{}>,
  arg: number,
) => {
  try {
    // eslint-disable-next-line @typescript-eslint/return-await
    return await callback(arg);
  } catch (err) {
    if (err instanceof Error) {
      return thunkAPI.rejectWithValue(err.message);
    }

    return thunkAPI.rejectWithValue('something went wrong');
  }
};

// eslint-disable-next-line max-len
export const fetchComments = createAsyncThunk('users/fetchComments', async (id: number, thunkAPI) => {
  tryCatchMiddleware(getPostComments, thunkAPI, id);
});

项目依赖

"dependencies": {
    "@reduxjs/toolkit": "^1.8.3",
    "axios": "^0.27.2",
    "bulma": "^0.9.4",
    "classnames": "^2.3.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-redux": "^8.0.2",
    "react-router-dom": "^6.3.0",
    "react-scripts": "5.0.1"
  },
  "devDependencies": {
    "@cypress/react": "^5.12.4",
    "@cypress/webpack-dev-server": "^1.8.4",
    "@types/node": "^17.0.45",
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "cypress": "^9.5.3",
    "eslint": "^7.32.0",
    "eslint-plugin-cypress": "^2.11.2",
    "gh-pages": "^3.2.3",
    "mochawesome": "^7.1.3",
    "mochawesome-merge": "^4.2.0",
    "mochawesome-report-generator": "^6.2.0",
    "node-sass": "^7.0.1",
    "stylelint": "^13.13.1",
    "typescript": "^4.6.3"
  },

解决方案

1. 修复类型错误与通用函数写法

Redux Toolkit中不存在GetThunkAPI全局类型,我们可以通过TypeScript的Parameters工具类型获取Async Thunk回调中thunkAPI的正确类型,同时用泛型让通用函数适配不同的参数和返回值:

import { AsyncThunkPayloadCreator } from '@reduxjs/toolkit';

// 通用try-catch包装函数
const withTryCatch = <Arg, Return, RejectValue = string>(
  callback: (arg: Arg) => Promise<Return>,
  thunkAPI: Parameters<AsyncThunkPayloadCreator<Return, Arg, any>>[1],
  arg: Arg
): Promise<Return | RejectValue> => {
  try {
    return callback(arg);
  } catch (err) {
    if (err instanceof Error) {
      return thunkAPI.rejectWithValue(err.message);
    }
    return thunkAPI.rejectWithValue('something went wrong');
  }
};

然后修改各个Async Thunk的实现,必须return通用函数的执行结果以确保返回类型正确:

export const fetchComments = createAsyncThunk('users/fetchComments', async (id: number, thunkAPI) => {
  return withTryCatch(getPostComments, thunkAPI, id);
});

export const addComment = createAsyncThunk('users/addComment', async (data: CommentData, thunkAPI) => {
  return withTryCatch(createComment, thunkAPI, data);
});

export const removeComment = createAsyncThunk('users/removeComment', async (id: number, thunkAPI) => {
  return withTryCatch(async (id) => {
    await deleteComment(id);
    return id;
  }, thunkAPI, id);
});

2. 进一步封装:直接生成带错误处理的Async Thunk

可以把通用逻辑和createAsyncThunk封装在一起,进一步减少重复代码:

import { createAsyncThunk, AsyncThunkPayloadCreator } from '@reduxjs/toolkit';

const createThunkWithTryCatch = <Arg, Return, RejectValue = string>(
  typePrefix: string,
  callback: AsyncThunkPayloadCreator<Return, Arg, any>
) => {
  return createAsyncThunk(typePrefix, async (arg: Arg, thunkAPI) => {
    try {
      return await callback(arg, thunkAPI);
    } catch (err) {
      if (err instanceof Error) {
        return thunkAPI.rejectWithValue(err.message);
      }
      return thunkAPI.rejectWithValue('something went wrong');
    }
  });
};

// 使用方式
export const fetchComments = createThunkWithTryCatch('users/fetchComments', (id) => getPostComments(id));
export const addComment = createThunkWithTryCatch('users/addComment', (data) => createComment(data));
export const removeComment = createThunkWithTryCatch('users/removeComment', async (id) => {
  await deleteComment(id);
  return id;
});

最佳实践建议

  • 保持类型安全:利用TypeScript泛型让通用函数适配不同的参数、返回值类型,避免使用any类型。
  • 灵活扩展错误处理:如果需要针对不同请求返回自定义错误信息,可以给通用函数添加可选的错误消息参数,或者根据axios错误的响应状态码做差异化处理。
  • 避免过度封装:如果项目中只有少量Async Thunk,封装带来的收益可能有限;但如果有大量重复的错误逻辑,封装能显著提升代码可维护性。
  • 统一错误格式:可以在通用函数中把错误标准化为固定格式的对象,方便后续在reducer或组件中统一处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:10:27