【问题标题】:TypeScript type of an async dispatch value异步调度值的 TypeScript 类型
【发布时间】:2021-02-07 23:58:46
【问题描述】:

我有以下异步操作定义:

import {Dispatch} from 'react';
import axios, {AxiosResponse, AxiosError} from 'axios';

function asyncAction() {
    return (dispatch: Dispatch<any>): Promise<number> => {
        return axios.get('http://www.example.com')
            .then( (res: AxiosResponse<any>) => {
                return 1;
            })
            .catch( (err: AxiosError<any>) => {
                return 2;
            });
    }
}

上面的类型检查没问题。

我也明白,当您调用 dispatch 并将异步操作传递给它时,如下所示:

dispatch(asynAction())

…然后是内部函数的返回类型,所以我希望上述值的类型是Promise&lt;number&gt;。然而,以下内容不会进行类型检查:

function foo (dispatch: Dispatch<any>) {
    const n: Promise<number> = dispatch(asyncAction()); // line A
}

具体来说,我在line A 收到以下错误:

TS2322: Type 'void' is not assignable to type 'Promise<number>'

所以,为了满足TS,我不得不做如下感觉不对劲的事情:

const n: Promise<number> = dispatch(asyncAction()) as unknown as Promise<number>;

我错过了什么?

更新

我的 `package.json` 有:
"@types/react-redux": "^7.1.9",
"react-redux": "^7.2.0",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0"

当我执行以下操作时:

import {ThunkDispatch as Dispatch} from 'redux-thunk';

...并使用导入的ThunkDispatch 类型作为ThunkDispatch&lt;any, any, any&gt;(在上面的代码中我有Dispatch&lt;any&gt;),如下所示:

import axios, {AxiosResponse
             , AxiosError} from 'axios';
import {ThunkDispatch as Dispatch} from 'redux-thunk';

export function asyncAction() {
    return (dispatch: Dispatch<any, any, any>): Promise<number> => {
        return axios.get('http://www.example.com')
            .then( (res: AxiosResponse<any>) => {
                return 1;
            })
            .catch( (err: AxiosError<any>) => {
                return 2;
            });
    }
}

export function foo (dispatch: Dispatch<any, any, any>) {
    const n: Promise<number> = dispatch(asyncAction());
    console.log(n);
}

…我得到一个不同的错误:

  TS2739: Type '(dispatch: ThunkDispatch<any, any, any>) => Promise<number>' is missing the following properties from type 'Promise<number>': then, catch, [Symbol.toStringTag]

【问题讨论】:

  • Dispatch&lt;any&gt; 不接受承诺。所以要么输入错误,要么你使用的库不正确。
  • 我认为您使用了错误的 Dispatch。应该是import { Dispatch } from "redux"
  • 我认为你应该从 thunk 导入 dispatch type
  • @HMR 我有redux-thunk 2.30,那里没有Dispatch 类型。我导入了一个ThunkDispatch 类型并用作ThunkDispatch&lt;any, any, any&gt;,因为该类型是通用的并且需要三个类型参数(我不知道它们代表什么)。我收到一条不同的错误消息,但仍然没有运气。我会更新问题。
  • 你能添加一个codesandbox吗?很难看出这一切是如何联系起来的,现在有点混乱。

标签: reactjs typescript redux axios redux-thunk


【解决方案1】:

不同的包对Dispatch 类型有不同的定义。您正在从“react”导入一个,这不适合您的需求。

Redux 声明一个 dispatch 返回 action:

export interface Dispatch<A extends Action = AnyAction> {
  <T extends A>(action: T): T
}

React 定义了 Dispatch 用于它自己的 useReducer 钩子(redux-lite),它说它什么都不返回:

type Dispatch<A> = (value: A) => void;

Thunk 有一个更复杂的定义,允许它基于泛型返回任意返回类型:

export interface Dispatch<A extends Action = AnyAction> {
    <TReturnType = any, TState = any, TExtraThunkArg = any>(
      thunkAction: ThunkAction<TReturnType, TState, TExtraThunkArg, A>,
    ): TReturnType;
  }

您的第一个示例有效,因为您从未真正调用过dispatch 函数,并且您的返回类型与axios 调用的返回类型相匹配。您在第二个示例中遇到了同样的问题。

asyncAction() 不会调度操作。 Promise&lt;number&gt; 是动作创建者的返回类型,而不是调度的返回类型。关于缺少属性的错误基本上是告诉你调度没有返回Promise

您以一种非常混乱和令人困惑的方式编写了asyncAction,但基本上它是一个返回一个函数的函数,该函数接受dispatch 并返回Promise&lt;number&gt;。因此,基于此,您在foo 中获得Promise&lt;number&gt; 的方式不是通过使用您的函数调用dispatch,而是通过将dispatch 作为参数传递给通过调用ayncAction() 创建的函数。

export function foo (dispatch: Dispatch<any, any, any>) {
    const n: Promise<number> = asyncAction()(dispatch);
    console.log(n);
}

这当然没有意义,因为您仍然永远不会在任何地方发送任何东西。

Typescript Playground Link

编辑:

希望这对您要做的事情有所帮助。希望从dispatch 的返回值中获取n 只是为了测试,因为您不能也不应该这样做。

让我们定义我们最终将分发的动作,以及它的创建者。

import { Action } from "redux";

// normal action type
interface NumberAction extends Action {
    payload: number;
}

// normal action creator
function sendNumber(number: number): NumberAction {
    return { type: 'SEND_NUMBER', payload: number };
}

我们创建了一个ThunkAction 来发送这个NumberAction。它是一个将dispatch 作为参数并为最终调度的操作返回Promise 的函数。

const myThunkAction1: ThunkAction<Promise<NumberAction>, any, any, NumberAction> = (dispatch): Promise<NumberAction> => {
    return axios.get('http://www.example.com')
        .then((res: AxiosResponse<any>) => {
            return 1;
        })
        .catch((err: AxiosError<any>) => {
            return 2;
        })
        .then(number => dispatch(sendNumber(number)));
}

我们可以 dispatch 这个,但它返回 ThunkAction 本身。换句话说,调用dispatch 会返回一个接受dispatch 的函数。因此,您无法从调度 thunk 的返回值中获得任何有意义的信息。

export function foo(dispatch: Dispatch<any, any, any>) {
    // still doesn't return Promise<number>, returns the ThunkAction
    const returned = dispatch(myThunkAction2);
    // can only get to Promise<number> by calling the returned thunk, which would re-dispatch the action
    const n: Promise<number> = returned(dispatch, () => {}, {});
    console.log(n);
}

Second Playground Link

【讨论】:

  • 我无法访问两个 TS 游乐场链接。但是,我认为调用 dispatch(myThankAction1) 返回 Promise 而不是 thunk 本身。至少这是我在测试中看到的,即使 TS 抱怨(但在运行时,我确实得到了一个 Promise)。就像我说的,我无法访问 TS 游乐场链接,所以我无法使用您的代码。此外,在您的最后一个代码块上,您有 dispatch(myThunkAction2)(而不是 myThunkAction1)。
  • 无论如何,我设法在沙盒上重现了它,并专门针对类型问题发布了另一个问题:stackoverflow.com/q/64542213/274677
  • 哎呀,我在玩两个版本的 thunk 动作,如果你不知道的话。我没有检查任何运行时值。打字稿定义很清楚,它返回ThunkAction,所以我要离开了。
猜你喜欢
  • 2018-01-01
  • 2021-08-28
  • 2019-03-29
  • 2021-07-06
  • 1970-01-01
  • 2016-09-23
  • 2021-12-25
  • 2020-06-09
  • 1970-01-01
相关资源
最近更新 更多