【问题标题】:React TypeScript component with two different prop interfaces使用两个不同的 prop 接口 React TypeScript 组件
【发布时间】:2020-02-26 01:40:33
【问题描述】:

我想创建一个 React TypeScript 组件,其 props 是两个不同接口的联合。但是,当我这样做时,我会收到警告:

TS2339: Property 'color' does not exist on type 'PropsWithChildren<Props>'

如何创建一个结合两个不同 prop 接口的 React TypeScript 组件,同时能够解构这些 prop?谢谢!

sampleComponent.tsx:

import * as React from 'react';

interface SamplePropsOne {
  name: string;
}

interface SamplePropsTwo {
  color: string;
}

type Props = SamplePropsOne | SamplePropsTwo;

const SampleComponent: React.FC<Props> = ({ color, name }) => (
  color ? (
    <h1>{color}</h1>
  ) : (
    <h1>{name}</h1>
  )
);

export default SampleComponent;

【问题讨论】:

  • 为什么你不想只将一种类型的道具从父组件传递给SampleComponent?你可以通过&lt;SampleComponent name={name} /&gt;&lt;SampleComponent name={color} /&gt;
  • @AndriiGolubenko,请不要从字面上理解组件实现细节。问题更多是关于如何创建一个可以接受一系列不同prop接口的组件。
  • 我知道您只是举了一个例子。但是您要创建的不是组件。它就像一个组件结构,接受类型、数据,然后呈现所需的组件。

标签: reactjs typescript


【解决方案1】:

在 TypeScript 允许您从联合类型中读取 namecolor 之前,它需要一些证据来证明您正在使用正确类型的 Props(SamplePropsOneSamplePropsTwo)。有几种标准方法可以提供它。

一种是通过引入一个属性来区分联合的分支,从而使联合成为一个标记联合。这种类型检查就好了:

interface SamplePropsOne {
  type: 'one';
  name: string;
}

interface SamplePropsTwo {
  type: 'two';
  color: string;
}

type Props = SamplePropsOne | SamplePropsTwo;

const SampleComponent: React.FC<Props> = props => (
  props.type === 'one' ? (
    <h1>{props.name}</h1>
  ) : (
    <h1>{props.color}</h1>
  )
);

如果你把案例倒过来(就像我在写这篇文章时所做的那样!),那么 TypeScript 会抱怨。

如果某个属性的存在足以区分类型,那么可以使用in运算符:

interface SamplePropsOne {
  name: string;
}
interface SamplePropsTwo {
  color: string;
}
type Props = SamplePropsOne | SamplePropsTwo;

const SampleComponent: React.FC<Props> = props => (
  'color' in props ? (
    <h1>{props.color}</h1>
  ) : (
    <h1>{props.name}</h1>
  )
);

如果确定您拥有哪种类型的对象需要更复杂的逻辑,您可以编写user-defined type guard。关键部分是返回类型中的“is”:

function isSampleOne(props: Props): props is SamplePropsOne {
  return 'name' in props;
}

const SampleComponent: React.FC<Props> = props => (
  isSampleOne(props) ? (
    <h1>{props.name}</h1>
  ) : (
    <h1>{props.color}</h1>
  )
);

还值得注意的是,由于结构类型的工作方式,您的示例中的props 没有理由不能同时拥有name color

const el = <SampleComponent name="roses" color="red" />;  // ok

如果不允许这样做很重要,您需要使用一些稍微花哨的类型:

interface SamplePropsOne {
  name: string;
  color?: never;
}
interface SamplePropsTwo {
  color: string;
  name?: never;
}
type Props = SamplePropsOne | SamplePropsTwo;

ts-essentials 库有一个 XOR generic,可用于帮助构建这样的独占联合。

【讨论】:

    【解决方案2】:

    我想你要找的是intersection types

    替换这一行:

    type Props = SamplePropsOne | SamplePropsTwo;
    

    用这一行:

    type Props = SamplePropsOne & SamplePropsTwo;
    
    • 交集类型:将多个接口/类型合二为一

    • 联合类型:选择多个接口/类型之一

    编辑

    你想要的是不可能的(我认为)。您可以做的是在转换 props 之后在一行中解构每种类型:

    const SampleComponent: React.FC<Props> = props => {
      const { name } = props as SamplePropsOne;
      const { color } = props as SamplePropsTwo;
    
      return color ? <h1>{color}</h1> : <h1>{name}</h1>;
    };
    

    【讨论】:

    • 我希望用户能够提供name 道具或color 道具,但不能同时提供。
    • @Jimmy 哦,对不起。我编辑了这个问题。这是更多的代码,但我认为这里没有其他选择。此外,您可能需要考虑实现 2 个使用 3 个通用组件的不同组件。
    • @ysfaran,有什么想法吗? -> “我希望用户能够提供名称道具或颜色道具,但不能同时提供两者。” ? ,谢谢!
    • @Esther-我认为答案应该很清楚。要分解它,您需要使用联合类型:type Props = { name: string } | { color: string }。我不知道你的确切问题,所以我不能正确回答。考虑提出一个新问题!
    • 对不起,你是对的。它应该被定向到@Jimmy,他有同样的问题。谢谢!
    猜你喜欢
    • 2020-05-27
    • 2021-11-25
    • 2021-08-23
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 2017-10-15
    • 2017-06-22
    • 1970-01-01
    相关资源
    最近更新 更多