具有两个不同的 prop 接口的 React TypeScript 组件

IT技术 reactjs typescript
2021-04-29 15:54:30

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

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

如何创建一个包含两个不同 prop 接口并同时能够解构这些 props 的 React TypeScript 组件?谢谢!

样本组件.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;

在此处输入图片说明

2个回答

在 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>
  )
);

如果确定您拥有哪种类型的对象需要更复杂的逻辑,您可以编写一个用户定义的类型保护关键部分是返回类型中的“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 and 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泛型,可以用来帮助构建这样的独占联合。

我认为您正在寻找的是交叉点类型

替换这一行:

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>;
};