是否可以添加道具类型以根据另一个道具的存在或价值来对组件道具进行反应?

时间:2019-04-24 23:25:22

标签: reactjs typescript

我在基于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中定义的道具,那就太好了。

2 个答案:

答案 0 :(得分:2)

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

首先,让我们定义两个单独的可能类型集,将它们严格结合在一起,并编写一个type guard,告诉我们的React组件我们正在使用哪些道具。我将扩展您在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中进行检查。

答案 1 :(得分:1)

您可以使用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})