【问题标题】:Is it possible to add prop types to react component props based on the presence or value of another prop?是否可以添加道具类型以根据另一个道具的存在或价值来反应组件道具?
【发布时间】:2019-04-24 23:25:22
【问题描述】:

我在基于 Typescript 的应用程序中有一个相当复杂的 React 组件。只有当另一个道具存在/真实时才需要多组道具。有什么方法可以在 Typescript 中实现这一点吗?我还没有使用它进行太多高级打字。

我对有区别的工会进行了一些尝试,但我认为它并没有完全涵盖所有可能性,因为可能需要任何组合或不需要这些额外的道具组。

这就是我在这里的工作。

interface CommonProps {
  ...a bunch of props...
}

interface ManagedProps extends CommonProps {
  managed: true;
  ...props for only when managed is present/true...
}

interface ServerSideProps extends CommonProps {
  serverSide: true;
  ...props for only when serverSide is present/true...
}

interface Props = ???

基线是CommonProps 将应用于此组件。如果managedtrue,那么ManagedProps 也应该适用。如果serverSidetrue,那么ServerSideProps 也适用。
我还没有遇到这种情况,但是如果可选的道具组可以覆盖CommonProps中定义的道具,那就太好了。

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    我使用ts-toolbeltUnion.Strict 助手取得了巨大的成功。

    首先,让我们定义两组不同的可能类型,将它们组合在一个严格的联合中并编写一个type guard,告诉我们的 React 组件我们正在使用哪一组 props。我将扩展您在 OP 中使用的示例:

    import { Union } from 'ts-toolbelt';
    
    interface CommonProps {
      commonProp: string
    }
    
    interface ManagedProps extends CommonProps {
      managed: true;
      managedBoolean: boolean;
      managedNumber: number;
    }
    
    interface ServerSideProps extends CommonProps {
      serverSide: true;
      serverBoolean: boolean;
      serverNumber: number;
    }
    
    const isManaged = (props: Props): props is ManagedProps => {
      return props.managed;
    }
    
    type Props = Union.Strict<ManagedProps | ServerSideProps>
    

    现在,让我们编写一个利用我们编写的 type guard 的 React 组件:

    const OurComponent: React.FC<Props> = (props) => {
      if (isManaged(props)) {
        return <div>Managed {props.commonProp}</div>
      } else {
        return <div>ServerSide {props.commonProp}</div>
      }
    }
    

    如果您使用错误的类型,这将导致错误。您可以在我创建的this StackBlitz 中查看。

    【讨论】:

      【解决方案2】:

      您可以使用conditional types 来获得类似的东西。例如:

      interface CommonProps {
          foo: string;
      }
      
      interface ManagedProps extends CommonProps {
          managed: true;
          managedOnlyProp: boolean;
      }
      
      interface ServerSideProps extends CommonProps {
          serverSide: true;
          serverOnlyProp: boolean;
      }
      
      type MappedProps<T> =
          T extends { managed: true } ? ManagedProps :
          T extends { serverSide: true } ? ServerSideProps :
          CommonProps;
      
      // Example function to test this:
      
      declare function takeProps<T>(props: T & MappedProps<T>): void;
      
      // And:
      
      // This is OK, neither flag is present, so CommonProps are used:
      takeProps({ foo: "" })
      
      // This is an error. The managed flag is present, so we must also include the managed only props:
      takeProps({ foo: "", managed: true }) // Gives error: Property 'managedOnlyProp' is missing in type '{ foo: string; managed: true; }' but required in type 'ManagedProps'.
      
      // But this is okay, the flag is present but set to false...
      takeProps({ foo: "", managed: false })
      
      // And likewise for the serverSide versions:
      takeProps({ foo: "", serverSide: true }) // Gives error: Property 'serverOnlyProp' is missing in type '{ foo: string; serverSide: true; }' but required in type 'ServerSideProps'.
      takeProps({ foo: "", serverSide: true, serverOnlyProp: false }) // OK, no errors
      takeProps({ foo: "", serverSide: false }) // OK, no errors
      

      这是Playground Link

      请注意,这仅适用于每个标志。组合使用时,仍然可以传递一个无效的对象:

      // This is wrong (missing serverOnlyProp) but no error is produced... :(
      takeProps({foo: "", serverSide: true, managed: true, managedOnlyProp: false})
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-19
        • 1970-01-01
        • 2018-05-03
        • 2019-07-23
        • 2020-10-09
        • 2021-06-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多