异步调度值的 TypeScript 类型

IT技术 reactjs typescript redux axios redux-thunk
2021-05-24 10:12: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<number>. 然而,以下不会进行类型检查:

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<any, any, any>(无论我Dispatch<any>在上面代码中的任何位置),如下所示:

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]
1个回答

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

Redux 声明调度返回操作:

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<number>action creator的返回类型,而不是 dispatch的返回类型关于缺少属性的错误基本上是告诉你调度没有返回 a Promise

asyncAction以一种非常混乱和令人困惑的方式编写,但基本上它是一个函数,它返回一个接受 adispatch并返回Promise<number>. 因此,基于这一点,你得到的方式Promise<number>foo不调用dispatch你的函数,而是通过传递dispatch作为参数来调用创建的函数ayncAction()

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

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

typescript游乐场链接

编辑:

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

让我们定义我们最终要调度的动作,以及它的创建者。

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发送 this 的NumberAction它是一个函数,它接受dispatch一个参数并Promise为它最终调度的动作返回一个 a

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);
}

第二个游乐场链接