Reactjs React钩子依赖项数组与TypeScript区分的联合

Reactjs React钩子依赖项数组与TypeScript区分的联合,reactjs,typescript,react-hooks,Reactjs,Typescript,React Hooks,我的React+TypeScript应用程序有一个组件,其道具类型是有区别的联合。这有助于防止编译时道具的无效组合。但是如果我需要将这些道具传递到React钩子的依赖列表中,那么正确的方法是什么?钩子的规则说我不能在条件中调用钩子,但是如果它们不在条件(也称为类型保护)中,则有区别的道具将导致TS编译器错误 我考虑了以下解决方法,所有这些方法似乎都会降低代码的可读性和/或安全性: 拆分为多个组件,并将挂钩推入每个实现中。这将复制大量代码 将每个区分的属性提取到局部变量中,例如const valu

我的React+TypeScript应用程序有一个组件,其道具类型是有区别的联合。这有助于防止编译时道具的无效组合。但是如果我需要将这些道具传递到React钩子的依赖列表中,那么正确的方法是什么?钩子的规则说我不能在条件中调用钩子,但是如果它们不在条件(也称为类型保护)中,则有区别的道具将导致TS编译器错误

我考虑了以下解决方法,所有这些方法似乎都会降低代码的可读性和/或安全性:

  • 拆分为多个组件,并将挂钩推入每个实现中。这将复制大量代码
  • 将每个区分的属性提取到局部变量中,例如
    const values=p.isMulti==true&&p.values
    并在依赖项列表中使用该属性。除了看起来不必要的冗长之外,这种方法还使得以后的代码可以绕过TypeScript的类型检查
  • 在依赖项列表中,将普通属性访问(例如
    p.value
    )替换为类型安全表达式,例如
    p.isMulti==true和&p.values
    。这里的问题是React的详尽依赖项ESLint规则抱怨(“React Hook React.usemo在依赖项数组中有一个复杂的表达式。将其提取到一个单独的变量,以便可以对其进行静态检查”),因为这些表达式不可静态测试。我必须禁用详尽的dep规则,这是我想要避免的,因为以后的问题不会被ESLint捕获
  • 在依赖项列表中添加类型强制转换以删除TS错误。这会触发与上述类型guard表达式相同的来自ESLint的详尽deps投诉
  • 更改区分的并集,使所有道具显示在两个分支中,但要求“错误”分支的值始终为
    readonly
    undefined
    。这将解决挂钩问题,但会使组件的类型安全性有所降低
  • 重构组件以移除鉴别器道具,并将并集向下推到每个道具中。这在下面的人为示例中是很容易的(例如,单个
    道具可以是
    字符串
    字符串[]
    ),但在我的实际应用中,联合要复杂得多,因此此“合并道具”选项将使组件更难正确使用
  • 将类型转换应用于deps列表中的道具,例如(
    p.value as string | undefined
    。这似乎是目前最不受欢迎的选项
  • 有更好的选择吗

    下面是一个人为的示例,它说明了问题:。下面重复了代码

    import*as React from'React';
    从'react dom'导入{render};
    类型道具=
    | {
    isMulti:正确;
    值:字符串[];
    }
    | {
    isMulti:错误;
    值:字符串;
    };
    常数X=(p:Props)=>{
    返回React.usemo(
    () => (
    价值:
    {p.isMulti==true&&p.values.join(',')}
    {p.isMulti==false&&p.value}
    ),
    [p.isMulti,p.values,p.value],//TS p.value/p.values上的错误
    );
    };
    函数App(){
    返回(
    );
    }
    const rootElement=document.getElementById('root');
    render(,rootElement);
    
    代码>

    这里的问题类型代码是:<代码> P.Value和P.Value/Cord>是您的类型定义相互排斥的。相反,请考虑以下类型:

    type Props =
      | {
          isMulti: true;
          value: undefined
          values: string[];
        }
      | {
          isMulti: false;
          value: string;
          values: undefined
        };
    
    与此类似,TS将推断值总是以其类型或未定义的形式存在(这是无论如何都不存在的属性的默认值)TS也正确地推断了它们的类型,例如,如果
    isMulti
    为真,
    value
    将如预期的那样
    未定义

    另一种选择是放弃整个价值/价值方面:

    type Props =
      | {
          isMulti: true;
          value: string[]
        }
      | {
          isMulti: false;
          value: string;
        };
    
    在这种情况下,您可以只使用
    value
    ,而不使用两个名称相似的属性。

    选项(5)根据我的回答,我认为这是你最好的选择。我还认为它的类型安全性并不比其他任何东西差;如果不检查你的判别属性,你就不会知道哪个属性是正确的,这与
    typeof
    instanceof
    检查没有什么不同。因为你需要
    usemo
    的所有属性别无选择。