Typescript 显然为分派的 Thunk 推断出错误的类型

Posted

技术标签:

【中文标题】Typescript 显然为分派的 Thunk 推断出错误的类型【英文标题】:Typescript apparently deduces the wrong type for a dispatched Thunk 【发布时间】:2021-02-09 00:14:54 【问题描述】:

完整的代码框示例 here

我声明了一个简单的动作类型和一个调度它的异步 thunk:

type ActionType =  type: "foo"; v: number ;

const FooThunk: ThunkAction<Promise<ActionType>, any, any, ActionType> = (dispatch): Promise<ActionType>
=> 
  return new Promise<number>((resolve) => 
    setTimeout(() => 
      resolve(42);
    , 100);
  ).then((v: number) => 
    return dispatch( type: "foo", v );
  );
;

现在的问题是当我调用dispatch(FooThunk) 时我得到的值的类型是什么。 Typescript 认为类型是 ThunkAction&lt;Promise&lt;ActionType&gt;, any, any, ActionType&gt; 并抱怨(在沙箱的第 38 行)并显示以下消息:

'ThunkAction<Promise<ActionType>, any, any, ActionType>' is missing the following properties from type 'Promise<ActionType>': then, catch, [Symbol.toStringTag]ts(2739)

但是,当我记录运行时获得的值时(codeandbox 的第 48 行),我清楚地看到它是 Promise。在 *** 上搜索我发现了相互矛盾的答案。 This answer 表示调度 thunk 会返回 thunk 本身。而this answer 建议调度 thunk 会返回一个 Promise。

Typescript 的类型系统似乎说调度 thunk 的类型与 thunk 本身相同。但是在运行时我得到一个 Promise 对象。我错过了什么?

仅出于完整性目的,我附加了您将找到沙箱的代码(上面提供的链接):

import * as React from "react";
import  render  from "react-dom";
import  Provider  from "react-redux";
import  createStore  from "redux";
import  initialState, rootReducer  from "./rootReducer";
import "./styles.css";

import  ThunkDispatch as Dispatch, ThunkAction  from "redux-thunk";
import  connect, ConnectedProps  from "react-redux";

import  applyMiddleware  from "redux";

import thunk from "redux-thunk";

const store = createStore(
  rootReducer /* preloadedState, */,
  applyMiddleware(thunk)
);

//const store = createStore(rootReducer, initialState);

type ActionType =  type: "foo"; v: number ;

const FooThunk: ThunkAction<Promise<ActionType>, any, any, ActionType> = (
  dispatch
): Promise<ActionType> => 
  return new Promise<number>((resolve) => 
    setTimeout(() => 
      resolve(42);
    , 100);
  ).then((v: number) => 
    return dispatch( type: "foo", v );
  );
;

const mapDispatchToProps = (dispatch: Dispatch<any, any, any>) => 
  return 
    dispatchFooThunk: (): Promise<ActionType> => dispatch(FooThunk)
  ;
;
const connector = connect(null, mapDispatchToProps);

type PropsFromRedux = ConnectedProps<typeof connector>;

class FooComponent_ extends React.Component<PropsFromRedux> 
  componentDidMount() 
    const p = this.props.dispatchFooThunk();
    console.log(p); // if you examine log output you see clearly that this is a PROMISE !
  
  render() 
    return <div>foo</div>;
  


const FooComponent = connector(FooComponent_);

class App extends React.Component 
  render() 
    return (
      <Provider store=store>
        <FooComponent />
      </Provider>
    );
  


const rootElement = document.getElementById("root");
render(<App />, rootElement);

【问题讨论】:

【参考方案1】:

thunk 的返回值是 thunk 动作本身的返回值。结果:如果它是一个异步函数,它会返回一个 Promise。

虽然普通的Dispatch 类型不知道如何处理thunk。 如果您使用的是 redux 工具包,this documentation part 描述了如何使用从商店配置中推断出的 Dispatch 类型正确键入您的 useDispatch 钩子。 如果你使用的是普通的 redux,你可以使用 ThunkDispatch 类型。 This part of the react-redux documentation 描述了在这种情况下如何键入 useDispatch 钩子。

PS:在您的具体情况下,您的 ThunkDispatch&lt;any, any, any&gt; 太笼统了。最后一个any 匹配得太急切了。改用ThunkDispatch&lt;any, any, AnyAction&gt; 即可。

【讨论】:

PS:为您的问题添加了修复程序。此外,如果您现在只是在使用 typescript 学习 redux,请查看 official redux toolkit,因为这将大大减少您的样板文件(尤其是 TypeScript 类型)并防止很多常见错误。 是的,我只是自己注意到了这一点,然后回来发表评论。我不明白,但它有效。但是,您提供的链接没有说明 ThunkDispatch 或如何键入它。我的代码沙盒已经在使用 ConnectedProps 等,但这似乎没有帮助。 是的,我一开始没有注意到,因为我错过了CodeSandbox,对不起^^ 至于any上的“为什么”:看看type signature:如果A 参数也匹配一个 thunk(any 匹配),它将采用第一个重载,否则为第二个。

以上是关于Typescript 显然为分派的 Thunk 推断出错误的类型的主要内容,如果未能解决你的问题,请参考以下文章

Redux thunk:从分派的动作返回承诺

使用 TypeScript 在 React 组件外部调度 Redux Thunk 操作

如何将 Redux 4.0 TypeScript 绑定与 redux-thunk 一起使用

如何在 Typescript 中使用 redux-thunk 使用 ThunkAction 正确键入 thunk?

Thunk 动作创建者派出另一个 thunk 动作创建者,但 typescript 抛出错误。我应该添加啥类型?

如何使用 redux-thunk 和 TypeScript 调度 ThunkAction