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

TypeScript中如何正确类型化createAsyncThunk消除action的any类型

解决方法

你遇到的报错根源有两个:

  1. 你当前写的createAsyncThunk没有显式声明返回值类型,且请求失败分支没有抛出错误,TS推断返回值可能为undefined,导致action.payload可能为空
  2. 原有Todo类型定义过于宽松,所有属性都是可选值,无法做精准类型校验

第一步:定义精准的类型

首先替换原有模糊的propsPayload定义,拆分出清晰的实体类型和请求参数类型:

// 定义Todo实体的精准类型
interface Todo {
  id: string;
  title: string;
  completed: boolean;
}

// 各个异步请求的参数类型
type GetTodosParams = void;
type AddTodoParams = Pick<Todo, 'title'>;
type ToggleCompleteParams = Pick<Todo, 'id' | 'completed'>;
type DeleteTodoParams = Pick<Todo, 'id'>;

第二步:给createAsyncThunk补充泛型和错误抛出

createAsyncThunk的泛型规则为:createAsyncThunk<返回值类型, 入参类型>,同时在请求失败分支抛出错误,保证fulfilled状态下payload一定存在:

export const getTodosAsync = createAsyncThunk<{ todos: Todo[] }, GetTodosParams>(
  'todos/getTodosAsync',
  async () => {
    const resp = await fetch('http://localhost:7000/todos');
    if (resp.ok) {
      const todos = (await resp.json()) as Todo[];
      return { todos };
    }
    // 请求失败抛出错误,会走rejected分支,不会进入fulfilled回调
    throw new Error('获取Todo列表失败');
  }
);

export const addTodoAsync = createAsyncThunk<{ todo: Todo }, AddTodoParams>(
  'todos/addTodoAsync',
  async (payload) => {
    const resp = await fetch('http://localhost:7000/todos', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ title: payload.title }),
    });

    if (resp.ok) {
      const todo = (await resp.json()) as Todo;
      return { todo };
    }
    throw new Error('添加Todo失败');
  }
);

export const toggleCompleteAsync = createAsyncThunk<{ todo: Todo }, ToggleCompleteParams>(
  'todos/completeTodoAsync',
  async (payload) => {
    const resp = await fetch(`http://localhost:7000/todos/${payload.id}`, {
      method: 'PATCH',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ completed: payload.completed }),
    });

    if (resp.ok) {
      const todo = (await resp.json()) as Todo;
      return { todo };
    }
    throw new Error('更新Todo状态失败');
  }
);

export const deleteTodoAsync = createAsyncThunk<{ id: string }, DeleteTodoParams>(
  'todos/deleteTodoAsync',
  async (payload) => {
    const resp = await fetch(`http://localhost:7000/todos/${payload.id}`, {
      method: 'DELETE',
    });

    if (resp.ok) {
      return { id: payload.id };
    }
    throw new Error('删除Todo失败');
  }
);

第三步:调整初始化状态和reducers类型

替换原有模糊的初始化状态,同步调整普通reducers的类型校验:

// 初始化状态改为空数组,符合Todo类型
const initialState = [] as Todo[];

export const todoSlice = createSlice({
  name: 'todos',
  initialState: initialState,
  reducers: {
    addTodo: (state, action: PayloadAction<AddTodoParams>) => {
      const todo: Todo = {
        id: nanoid(),
        title: action.payload.title,
        completed: false,
      };
      state.push(todo);
    },
    toggleComplete: (state, action: PayloadAction<ToggleCompleteParams>) => {
      const index = state.findIndex((todo) => todo.id === action.payload.id);
      if (index !== -1) {
        state[index].completed = action.payload.completed;
      }
    },
    deleteTodo: (state, action: PayloadAction<DeleteTodoParams>) => {
      return state.filter((todo) => todo.id !== action.payload.id);
    },
  },
  // 这里直接去掉action的any即可,TS会自动推断正确的payload类型
  extraReducers: (builder) => {
    builder
      .addCase(getTodosAsync.fulfilled, (state, action) => {
        return action.payload.todos;
      })
      .addCase(addTodoAsync.fulfilled, (state, action) => {
        state.push(action.payload.todo);
      })
      .addCase(toggleCompleteAsync.fulfilled, (state, action) => {
        const index = state.findIndex(
          (todo) => todo.id === action.payload.todo.id
        );
        if (index !== -1) {
          state[index].completed = action.payload.todo.completed;
        }
      })
      .addCase(deleteTodoAsync.fulfilled, (state, action) => {
        return state.filter((todo) => todo.id !== action.payload.id);
      });
  }
});

改完之后就可以完全去掉所有any类型,不会再出现payload可能为undefined的报错,同时所有类型校验都是精准的。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 21:54:03