Не могу найти способ сделать условные реквизиты

На данный момент у меня есть два компонента:

export interface MediaCardProps {
  alt: string;
  src: string;
}

export const MediaCard = ({ alt, src }: MediaCardProps) => {
  ...
};

и

export interface TextCardProps {
  isLoaded: boolean;
  children: ReactNode;
}

export const TextCard = ({ children, isLoaded = false }: TextCardProps) => {
   ...
};

Я хотел бы создать родительский компонент следующим образом:

type Props = ({ isMedia: false } & TextCardProps) | ({ isMedia: true } & MediaCardProps);

export const Card = ({ isMedia = false, ...args }: Props) => {
  if (isMedia) return <MediaCard {...(args as MediaCardProps)} />;

  return <TextCard {...(args as TextCardProps)} />;
};

Цель здесь состоит в том, чтобы, когда для isMedia установлено значение true, мы видели только MediaCardProps, в противном случае мы видели TextCardProps.

Но при этом у меня возникла проблема при вызове. Я называю это так:

<Card isLoaded = {!!data.title}>
  <h1 className='text-2xl font-bold'>{data.title}</h1>
</Card>

и у меня такая ошибка Type '{ children: Element; isLoaded: boolean; }' is not assignable to type 'IntrinsicAttributes & Props'. Property 'isMedia' is missing in type '{ children: Element; isLoaded: boolean; }' but required in type '{ isMedia: false; }'.

Мне удалось это исправить, изменив родительский реквизит на это:

type Props = { isMedia?: false; args: TextCardProps } | { isMedia: true; args: MediaCardProps };

но мне не нужна опора args.

Большое спасибо, если вы можете помочь мне это исправить!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Компонент вашей карты должен получить информацию о запрошенном типе носителя. Этого можно достичь, определив компонент Card как универсальный компонент, который принимает CardType в качестве универсального параметра.

Та же логика применима и к логическому типу, но для большей гибкости я заменил логический тип объединением типов:

type CardType = 'media' | 'text'

Теперь сделайте компонент Card универсальным:

const Card = <TCardType extends CardType>(props: Props<TCardType>) => { ... }

Мы передаем TCardType как общий тип в Props. Чтобы вывести его фактическое значение, нам нужно определить условную логику внутри нашего динамического универсального типа Props.

type Props<CardType> = { type?: CardType } & (CardType extends 'media' ? MediaCardProps : TextCardProps)

{ type?: CardType } позволяет сделать тип необязательным (по умолчанию 'text') и указывает тип, определяемый пользователем. Будет выведен тип этого поля, что позволит Props обеспечить правильную структуру для фактической Card реализации (с isMedia логическим значением вместо этого вы бы проверили T extends true).

Наконец, внутри общего компонента Card нам нужно только проверить, с каким реквизитом мы имеем дело. На основе этой информации мы можем вернуть соответствующую реализацию Card:

const Card = <TCardType extends CardType>(props: Props<TCardType>) => {
  if (isMediaProps(props)) {
    return <MediaCard {...props} />;
  }
  if (isTextProps(props)) {
    return <TextCard {...props} />;
  }
  return null
};

Вот вам площадка TS с готовым решением.

Прежде всего, большое спасибо, все было действительно ясно. У меня все еще есть проблема, когда я вызываю такие вещи, как <Card isLoaded = {true} alt=''>...</Card>. Я не смогу использовать параметр alt, поскольку он должен быть специфичен для типа носителя. Я делаю что-то неправильно ?

Matthieu 05.06.2024 14:47

Возможно, вам придется добавить CardType по умолчанию в универсальный компонент Card, чтобы всегда использовать text. const Card = <TCardType extends CardType = 'text'>(props: Props<TCardType>) => {. Я обновил ссылку TS Playground новым кодом.

Bruno Kawka 05.06.2024 16:30

Похоже, эта тема SO объясняет это хорошо stackoverflow.com/a/75562574/8997321

Bruno Kawka 05.06.2024 16:33

Другие вопросы по теме

Похожие вопросы

Есть ли способ визуализировать HTML из строки в React без использования dangerouslySetInnerHTML или пакета React HTML Parser?
Ошибка теста Playwright: реквизиты возвращают разные значения в пользовательском интерфейсе тестирования Playwright и в реальном приложении React в браузере Chrome
TypeScript › Обновление импорта при перемещении файла: включено, не работает в VS Code
Эффект масштабирования при наведении изображения, отображение деталей изображения
React DatePicker не закрывается после выбора даты
Автономная библиотека веб-пакетов React
Становится ли изучение useEffect() менее важным для извлечения данных в React? Должны ли новички отдавать приоритет изучению React Query, а не useEffect()?
Данные в модальном режиме не обновляются в React JS
Могу ли я передать дочерний компонент в качестве опоры от родительского. А потом это перейдет на следующих детей?
Тип номера MUI TextField мин. макс. не работает