【问题标题】:Flow - typesafe actionsFlow - 类型安全的动作
【发布时间】:2021-08-24 22:52:08
【问题描述】:

我正在使用 flow 并且我想让我的 reducer 更安全。我遇到了这个评论,它提出了一个我觉得很好并且适合我的代码库的解决方案: https://github.com/reduxjs/redux/issues/992#issuecomment-191152574

我正在尝试使用 %checks 关键字将其移植到流中,但它不起作用。

我的代码:

export type Action<T> = {
  type: string,
  payload: T,
};

interface ActionCreator<P> {
  type: string;
  (payload: P): Action<P>;
}

export function actionCreator<P>(type: string): ActionCreator<P> {
  return Object.assign((payload: P) => ({ type, payload }), { type });
}

export function isActionOfType<P>(
  action: Action<any>,
  creator: ActionCreator<P>
): boolean %checks {
  return action.type === creator.type;
}

每当我像这样在 reducer 函数中使用它时

(...)
case isActionOfType(action, getArticles):
  // action.payload is still any
(...)

我做错了吗?是否有可能让这个打字稿解决方案流畅运行?还是我应该使用不同的方法?如果是,您有什么建议?

【问题讨论】:

    标签: javascript typescript redux flowtype type-safety


    【解决方案1】:

    在 TypeScript 中,您不会使用 case 语句,而是使用 if 语句。

    没有规定你必须在 Redux 中使用 case 语句,事实上我们不再鼓励在 TypeScript 中使用这种模式,而是支持带有类型保护的 if 语句。

    所以我想你可以试试。很抱歉,我在流程方面无法真正帮助您,我只能就我们如何为 TypeScript 推荐现代模式提供一般性建议,并希望这些能反映在流程中。

    通常,您还可以查看官方的 Redux Toolkit,它带来了所有这些抽象。它只是部分流类型,但这些类型可能已经满足您的需求,或者您甚至可以改进它们。

    【讨论】:

    • 感谢您的信息,我肯定会从 switch 语句更改为 ifs 但是它应该适用于两种情况,我可以确认更改为 ifs 并没有改变任何东西 - 仍然得到 @ 987654326@ 而不是窄类型 :(
    • 然后看看 Redux Toolkit 中的createSlice。我认为这是输入的,无论如何应该照顾整个“编写减速器的东西”:)
    【解决方案2】:

    如果您构建的泛型类型具有一个类型字符串引用与有效负载类型链接,则可以使用切换案例。我不知道是否可以像您使用的方式那样使用案例,因为在 case 而不是 booleans 中测试 strings 的条件几乎是它们自己的。

    以下解决方案利用类型细化,因此根据您编写的条件 JS 流将推断有效负载结构。这里需要注意的是,在您的action 参数中,您必须添加所有可能的类型有效负载,否则流不知道可以从哪些结构中进行改进,这可以在其他地方分解,但这也有助于锁定您的变化减少你不想处理的情况。

    try-flow

    type AppT = {}
    
    type ActionT<T, P> = {|
      type: T,
      payload: P,
    |};
    
    type PayloadA = {| a: string |};
    type PayloadB = {| b: string |};
    
    const initialState = {};
    
    const reducer = (
      state: AppT = initialState,
      action: ActionT<'ACTION_A', PayloadA> | ActionT<'ACTION_B', PayloadB>,
    ): AppT => {
      switch (action.type) {
        case 'ACTION_A': {
          return {
            ...state,
            a: action.payload.a,
          };
        }
        case 'ACTION_B': {
          return {
            ...state,
            a: action.payload.a, // <-- it fails because type refines to PayloadB
            b: action.payload.b,
          };
        }
        default:
          return state;
      }
    };
    

    【讨论】:

    • 在我看来,这个解决方案在大型代码库中变得非常混乱。在您的示例中,它看起来不错,但我们只有 2 个有效负载选项。如果我们现在有一个更现实的场景,其中有 8-10 个动作怎么办? reducer 函数中的 action 参数会变得非常大。还是我遗漏了什么,有办法清理它?
    • 对于 TypeScript,我们明确建议不要使用分布式联合模式,因为它们比类型保护做的工作要多得多,并且总是包含对编译器的谎言(无论你如何输入,都会调用 reducer all 方法)实际影响代码流分析。据我了解,OP 明确希望在 flowtype 中使用类型保护( %checks ),所以我不确定在这里指向分布式联合是否有用。
    猜你喜欢
    • 2017-11-20
    • 2011-10-26
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 2013-10-11
    相关资源
    最近更新 更多