【问题标题】:Flow typing error : Cannot call dispatch with object literal bound to action because:流输入错误:无法使用绑定到操作的对象文字调用调度,因为:
【发布时间】:2020-05-19 07:58:14
【问题描述】:

我在 Flow 中收到以下错误:

无法使用绑定到操作的对象字面量调用调度,因为:

• 任一字符串文字 REMOVE_FROM_CART [1] 与属性类型中的字符串文字 ADD_TO_CART [2] 不兼容。

• 或字符串文字 ADD_TO_CART [1] 与属性类型中的字符串文字 REMOVE_FROM_CART [3] 不兼容。

• 或字符串文字 ADD_TO_CART [1] 与属性类型中的字符串文字 INCREMENT [4] 不兼容。

• 或字符串文字 ADD_TO_CART [1] 与属性类型中的字符串文字 DECREMENT [5] 不兼容。

• 或字符串文字 ADD_TO_CART [1] 与属性类型中的字符串文字 DISPLAY_PRODUCT_DETAIL [6] 不兼容。

我在下面标记为 ** dispatch ** 的调度函数中遇到错误

export const updateCart = (id: number, type: ProductActionsType) => {
    return (dispatch: Dispatch<ProductActionsReturnTypes>) => {
       **dispatch({ type, payload: id });**
       dispatch({ type: UPDATE_TOTALS });
     };
 };

文件:constants.js

export const FETCH_ITEMS = "FETCH_ITEMS";
export const ADD_TO_CART: "ADD_TO_CART" = "ADD_TO_CART";
export const REMOVE_FROM_CART: "REMOVE_FROM_CART" = "REMOVE_FROM_CART";
export const DISPLAY_PRODUCT_DETAIL: "DISPLAY_PRODUCT_DETAIL" =
  "DISPLAY_PRODUCT_DETAIL";
export const INCREMENT: "INCREMENT" = "INCREMENT";
export const DECREMENT: "DECREMENT" = "DECREMENT";
export const UPDATE_TOTALS: "UPDATE_TOTALS" = "UPDATE_TOTALS";
export const CLEAR_CART: "CLEAR_CART" = "CLEAR_CART";
export const OPEN_MODAL: "OPEN_MODAL" = "OPEN_MODAL";
export const CLOSE_MODAL: "CLOSE_MODAL" = "CLOSE_MODAL";

文件:src/types/index.js

export type ClearCartActionType = {|
type: typeof CLEAR_CART
|};

type AddToCartActionType = {|
type: typeof ADD_TO_CART,
payload: number
|};

type RemoveFromCartActionType = {|
type: typeof REMOVE_FROM_CART,
payload: number
|};

export type DisplayProductDetailAction = {|
type: typeof DISPLAY_PRODUCT_DETAIL,
payload: number
|};

type IncrementActionType = {|
type: typeof INCREMENT,
payload: number
|};

type DecrementActionType = {|
type: typeof DECREMENT,
payload: number
|};

export type UpdateTotalsActionType = {|
type: typeof UPDATE_TOTALS
|};

export type ModalPayloadType = {|
id: number,
products: Array<ProductType>
|};

export type ProductActionsType =
| typeof ADD_TO_CART
| typeof REMOVE_FROM_CART
| typeof INCREMENT
| typeof DECREMENT
| typeof UPDATE_TOTALS
| typeof DISPLAY_PRODUCT_DETAIL
| typeof CLEAR_CART;

export type ProductActionsReturnTypes =
| AddToCartActionType
| RemoveFromCartActionType
| IncrementActionType
| DecrementActionType
| UpdateTotalsActionType
| DisplayProductDetailAction
| ClearCartActionType;

我被这个困住了。任何人都可以提供解决方案吗?

重现问题的流程链接 link

【问题讨论】:

  • 如果您在try flow 页面上创建最小重现案例,其他人将更容易诊断问题。
  • 我无法在评论中分享这个案例。它说文本太长。因此,我在最后更新了问题本身的案例。请检查。 @josephjnk

标签: reactjs redux react-redux flowtype redux-thunk


【解决方案1】:

这不是一个正确的答案,而是一个替代方案。我无法让您的示例正常工作,并且通常发现在 Flow 中使用对象类型和传播很复杂,所以这就是我解决问题的方法。这是一个简化的示例,包括足够的动作来显示正在使用的样式。

type Dispatch<A, B> = A => B;

class AddToCartAction {
  payload: number;
  constructor(payload: number) {
    this.payload = payload;
  }
}

class RemoveFromCartAction {
  payload: number;
  constructor(payload: number) {
    this.payload = payload;
  }
}

class UpdateTotalsAction {}

/*
 * I removed the additional type annotations from the constants here,
 * and put their literal values in ProductActionsType instead. While
 * this adds some duplication, it works alongside Flow better. If the
 * annotations are used on the constants, and anyone forgets to add one
 * to a new constant, then ProductActionsType will have its type inferred
 * as "string", thus invisibly reducing type safety by allowing more
 * inputs than desired. By putting the literal strings in both the
 * constants and the types, the type system will catch you if you make
 * a typo, and it becomes more clear what is allowed as a
 * ProductActionsType.
 */
export const ADD_TO_CART = "ADD_TO_CART";
export const REMOVE_FROM_CART = "REMOVE_FROM_CART";
export const UPDATE_TOTALS = "UPDATE_TOTALS";

export type ProductActionsType =
| "ADD_TO_CART"
| "REMOVE_FROM_CART"
| "UPDATE_TOTALS";


export type ProductActionsReturnTypes =
| AddToCartAction
| RemoveFromCartAction
| UpdateTotalsAction;

export const updateCart = (id: number, type: ProductActionsType) => {
    return (dispatch: Dispatch<ProductActionsReturnTypes, void>) => {
        const action =
              type === "ADD_TO_CART"      ? new AddToCartAction(id)
        :     type === "REMOVE_FROM_CART" ? new RemoveFromCartAction(id)
        :     /* else UPDATE_TOTALS      */ new UpdateTotalsAction();
        dispatch(action);
        dispatch(new UpdateTotalsAction());
      };
 };

基本思想是,不是使用带有type 字段的对象,而是为每种类型创建一个类。 Flow 的类型细化非常非常擅长处理类,并且当多种类型没有融合为一个时,更容易理解类型签名和类型错误。

嵌套的三元表达式可以作为 switch/case 语句的替代方式来阅读,另外一个好处是它们可以处理条件中的任意布尔表达式。例如,我会使用如下代码处理操作:

function handleActions(a: ProductActionTypes) {
  return a instanceof AddToCartAction      ? handleAddToCart(a.payload)
  :      a instanceof RemoveFromCartAction ? handleRemoveFromCart(a.payload)
  :      /* a is an UpdateTotalsAction    */ handleUpdateTotals();
}

可以在MDN docs 三元表达式的“条件链”部分看到此语法。它与 Flow 配合得很好,它将知道条件句右侧每个中a 的类型。如果您忘记测试一个案例,Flow 甚至会知道,因为最后一个条件假设 ProductActionTypes 类型中的所有其他类都已被检查过。

希望这对您有所帮助,即使它不是您想要的。我用这种风格编写了我所有的 Flow 代码,并且自从我开始这样做以来,还没有出现任何非常粗糙、令人困惑的类型错误。

【讨论】:

  • 我明白你为什么直接放置字符串而不是常量。但是在多个位置重复“ADD_TO_CART”类型的值可能会导致拼写错误,如果我们稍后尝试更改 ADD_TO_CART 的名称,则必须在多个位置更改该值。
  • 幸运的是,这是类型系统的好处之一。虽然这两个位置确实耦合在一起,但 Flow 将确保它们正确保持同步,因为如果它们不同,则会发生类型错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-04
  • 1970-01-01
  • 2017-09-04
  • 2011-07-04
  • 2021-09-20
相关资源
最近更新 更多