如何正确使用带有 typescript 的 createAsyncThunk 函数?

Posted

技术标签:

【中文标题】如何正确使用带有 typescript 的 createAsyncThunk 函数?【英文标题】:How do I properly use createAsyncThunk function with typescript? 【发布时间】:2021-12-24 09:07:10 【问题描述】:

你可以找到完整的项目here。

我有以下代码:

    extraReducers: (builder) => 
        builder
            .addCase(getTodosAsync.fulfilled, (state, action:any) => 
                return action.payload.todos
            )
            .addCase(addTodoAsync.fulfilled, (state, action:any) => 
                state.push(action.payload.todo)
            )
            .addCase(toggleCompleteAsync.fulfilled, (state, action:any) => 
                const index = state.findIndex(
                    (todo) => todo.id === action.payload.todo.id
                )
                state[index].completed = action.payload.todo.completed
            )
            .addCase(deleteTodoAsync.fulfilled, (state, action:any) => 
                return state.filter((todo) => todo.id !== action.payload.id)
            )
    

但我想正确键入回调的动作参数,换句话说,摆脱“任何”类型。我已经发现正确的方法是键入 createAsyncThunk,但是直到现在我都不知道该怎么做。

文件的其余代码如下:

import 
    createAsyncThunk,
    createSlice,
    PayloadAction
 from '@reduxjs/toolkit';

// import AsyncThunkFulfilledActionCreator from '../../node_modules/@reduxjs/toolkit/src/createAsyncThunk'

import  nanoid  from 'nanoid';

interface propsPayload 
    title?: string,
    id?: string,
    completed?: boolean



const initialState = [
    
] as Array<propsPayload>

export const getTodosAsync = createAsyncThunk(
    'todos/getTodosAsync',
    async () => 
        const resp = await fetch('http://localhost:7000/todos');
        if (resp.ok) 
            const todos = (await resp.json()) ;
            return  todos  ;
        
    
);

export const addTodoAsync = createAsyncThunk(
    'todos/addTodoAsync',
    async (payload: propsPayload) => 
        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();
            return  todo  ;
        
    
);

export const toggleCompleteAsync = createAsyncThunk(
    'todos/completeTodoAsync',
    async (payload: propsPayload) => 
        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();
            return  todo  ;
        
    
);

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

        if (resp.ok) 
            return  id: payload.id ;
        
    
);


export const todoSlice = createSlice(
    name: 'todos',
    initialState: initialState,
    reducers: 
        addTodo: (state: Array<propsPayload>, action: PayloadAction<propsPayload>) => 
            const todo = 
                id: nanoid(),
                title: action.payload.title,
                completed: false,
            ;
            state.push(todo);
        ,
        toggleComplete: (state: Array<propsPayload>, action: PayloadAction<propsPayload>) => 
            const index = state.findIndex((todo) => todo.id === action.payload.id);
            state[index].completed = action.payload.completed;
        ,
        deleteTodo: (state: Array<propsPayload>, action: PayloadAction<propsPayload>) => 
            return state.filter((todo) => todo.id !== action.payload.id);
        ,
    ,
    extraReducers: (builder) => 
        builder
            .addCase(getTodosAsync.fulfilled, (state, action:any) => 
                return action.payload.todos
            )
            .addCase(addTodoAsync.fulfilled, (state, action:any) => 
                state.push(action.payload.todo)
            )
            .addCase(toggleCompleteAsync.fulfilled, (state, action:any) => 
                const index = state.findIndex(
                    (todo) => todo.id === action.payload.todo.id
                )
                state[index].completed = action.payload.todo.completed
            )
            .addCase(deleteTodoAsync.fulfilled, (state, action:any) => 
                return state.filter((todo) => todo.id !== action.payload.id)
            )
    
);

export const  addTodo, toggleComplete, deleteTodo  = todoSlice.actions;

export default todoSlice.reducer;



但如果我从操作中删除“任何”,就会发生这种情况:

Object is possibly 'undefined'.ts(2532)
(parameter) action: PayloadAction<
    todos: any;
 | undefined, string, 
    arg: void;
    requestId: string;
    requestStatus: "fulfilled";
, never>
Object is possibly 'undefined'.ts(2532)
(property) payload: 
    todos: any;
 | undefined

【问题讨论】:

您是否在redux-toolkit.js.org/usage/… 阅读了有关如何将其与打字稿一起使用的文档? 【参考方案1】:

根本不要输入动作类型。如果您什么都不做,则可以从您的 asyncThunk 类型正确推断。


            .addCase(addTodoAsync.fulfilled, (state, action) => 

就像state 自动是正确的类型一样,action 也是 - 由addTodoAsync 调度的已完成操作的类型。

【讨论】:

但是如果我删除第一个函数的'any',例如,会发生以下错误: Object is possible 'undefined'.ts(2532) (property) payload: todos: any; | undefined 没有可用的快速修复方法 因为这是正确的。由于if 语句,您的代码并不总是return。如果一个函数没有return,它会返回undefined 非常感谢!如果条件不成功,我编辑了代码以返回初始状态,并且它起作用了。

以上是关于如何正确使用带有 typescript 的 createAsyncThunk 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Typescript 中正确使用 React.lazy() 来导入带有泛型类型参数的反应组件?

如何正确继承 Typescript 中的父组件?

带有 Typescript 和 ThemeProvider 的样式化组件。啥是正确的类型?

如何在带有 TypeScript 的 React-Native 中使用 styled-component 键入无状态功能组件?

如何使 TypeScript 字符串枚举适用于字符串文字并正确进行类型推断

如何配置 VSCode 以运行 Yarn 2(使用 PnP)驱动的 TypeScript