代码之家  ›  专栏  ›  技术社区  ›  fraxture

流未检测块是否处理潜在的未定义(可能)值。为什么?

  •  0
  • fraxture  · 技术社区  · 6 年前

    我看到了一种我不理解的行为。我要键入的函数是redux reducer。我看到的问题似乎源于 action 提供给减速机的对象具有标记为“可能”运算符的有效负载: ?

    type Action = {
      type: 'foo',
      payload?: { ... }
    }
    

    在减速器中,我试图提供一个if门来处理 payload 未定义。从理论上讲,这似乎是可行的。最简单的情况是可行的(参见 here ):

    type Foo = {
      type: 'foo',
      payload?: {
        foo: 'foo'
      }
    };
    
    type Bar = {
      type: 'bar',
      payload?: {
        bar: 'bar',
      }
    };
    
    const reducer = (state: {} = {}, action: Foo | Bar) => {
      switch (action.type) {
        case 'foo': {
          if (!action.payload) {
            return state;
          }
          return action.payload.foo;
        }
        case 'bar': {
          if (!action.payload) {
            return state;
          }
          return action.payload.bar;
        }
        default:
          return state;
      }
    }
    

    然而,在我的实际减速机,这是一个有点复杂,我无法摆脱的错误。我的减速机看起来像这样:

    const camelize = (x) => x;
    const getSearchTypeFromPath = (x) => x;
    
    type A = {
      type: '@@router/LOCATION_CHANGE',
      payload?: {
        pathname: string,
        query: {},
      }
    }
    
    type B = {
      type: 'action.foo',
      payload?: {
        mode: string,
        params: {}
      }
    }
    
    const byMode = (
      state: {} = {},
      action: A | B,
    ) => {
      switch (action.type) {
        case '@@router/LOCATION_CHANGE': {
          if (!action.payload) {
            return state;
          }
    
          const modeKey: string = camelize(getSearchTypeFromPath(action.payload.pathname));
          return {
            ...state,
            [modeKey]: action.payload.query,
          };
        }
        case 'action.foo': {
          if (!action.payload) {
            return state;
          }
    
          const modeKey: string = camelize(action.payload.mode);
          return {
            ...state,
            [modeKey]: action.payload.params,
          }
        }
        default:
          return state;
      }
    };
    

    与上面的简化情况不同,此代码会产生一些流错误:请参见 here :

    33:         [modeKey]: action.payload.query,
                                          ^ Cannot get `action.payload.query` because property `query` is missing in undefined [1].
    References:
    6:   payload?: {               ^ [1]
    44:         [modeKey]: action.payload.params,
                                          ^ Cannot get `action.payload.params` because property `params` is missing in undefined [1].
    References:
    14:   payload?: {                ^ [1]
    

    有效载荷 我也这么认为。为什么会出错?有趣的是,每个case块中引用的第一个属性没有错误:即。 pathname mode . 最后,我注意到的另一件事是,如果我删除helper函数,在我的示例中,这些函数刚刚变成了伪函数,那么我就不会得到错误。见 here .

    3 回复  |  直到 6 年前
        1
  •  2
  •   Ryan Cavanaugh    6 年前

    问题是 camelize .

    action 是一个参数,所以发生这样的事情是合理的:

    function camelize(x) {
       someAction.payload = undefined;
    }
    
    // Now your code crashes
    byMode({}, someAction);
    

    一般来说,解决方法是将被测物提取到 const

    const payload = action.payload;
    if (payload === undefined) return;
    // OK to use 'payload' after function calls now
    

    另见 https://flow.org/en/docs/lang/refinements/#toc-refinement-invalidations 或例如。 https://github.com/facebook/flow/issues/5393

        2
  •  0
  •   Abhishek    6 年前
    type A = {
    type: '@@router/LOCATION_CHANGE',
    payload: {
      pathname: string,
      query: {},
     }
    }
    
    type B = {
    type: 'action.foo',
    payload: {
      mode: string,
      params: {}
     }
    }
    

    ? 但是通过删除这个将删除您的错误。

        3
  •  0
  •   fraxture    6 年前

    我唯一能让它起作用的方法是为这些属性的每个引用添加一些三元检查,即使在逻辑门之后,如下所示:

    const camelize = (x) => x;
    const getSearchTypeFromPath = (x) => x;
    
    type A = {
      type: '@@router/LOCATION_CHANGE',
      payload?: {
        pathname: string,
        query: {},
      }
    }
    
    type B = {
      type: 'action.foo',
      payload?: {
        mode: string,
        params: {}
      }
    }
    
    const byMode = (
      state: {} = {},
      action: A | B,
    ) => {
      switch (action.type) {
        case '@@router/LOCATION_CHANGE': {
          if (!action.payload) {
            throw new Error('Ooops');
          }
    
          const modeKey: string = camelize(getSearchTypeFromPath(action.payload ? action.payload.pathname : ''));
          return {
            ...state,
            [modeKey]: action.payload ? action.payload.query: {},
          };
        }
        case 'action.foo': {
          if (!action.payload) {
            return state;
          }
    
          const modeKey: string = camelize(action.payload.mode || '');
          return {
            ...state,
            [modeKey]: action.payload ? action.payload.params : {},
          }
        }
        default:
          return state;
      }
    };
    

    here

    我不明白为什么这样做,即使如果 action.payload 未定义。在这种情况下,需要满足流的代码似乎不太理想,因为它需要看起来不必要的检查。