Дочерние реквизиты Infer React в интерфейсе TypeScript

Можно ли вывести реквизиты первого дочернего элемента и применить их в TypeScript? Я могу подобрать его близко, но с дженериками он начинает давать сбой, и я не могу определить тип.

Я пытаюсь передать реквизиты компонента из оболочки первому дочернему компоненту с безопасностью типов. Если свойство не существует на объекте, TS должен завершиться неудачей, в противном случае он пройдет.

import React, {
  Children,
  isValidElement,
  cloneElement,
  ReactNode,
} from 'react';

interface WrapperProps<C> {
  children: ReactNode;
  // how can I make typeof Button generic/inferred from the code?
  // firstChildProps: Partial<React.ComponentProps<typeof Button>>; // works with <C> removed
  firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}

const Wrapper: React.FC<WrapperProps<typeof Button>> = ({
  children,
  firstChildProps,
}) => {
  const firstChild = Children.toArray(children)[0];
  if (isValidElement(firstChild)) {
    // Clone the first child element and pass the firstChildProps to it
    return cloneElement(firstChild, { ...firstChildProps });
  } else {
    return <>{children}</>;
  }
};

interface ButtonProps {
  disabled: boolean;
  children: ReactNode;
}

const Button: React.FC<ButtonProps> = ({ disabled, children }) => {
  return <button disabled = {disabled}>{children}</button>;
};

const Example = () => {
  return (
    <>
      {/* Passes type check because disabled exists on Button */}
      <Wrapper firstChildProps = {{ disabled: false }}>
        <Button disabled = {true}>Ok</Button>
      </Wrapper>
      {/* Fails type check because cheese does not exist on Button */}
      <Wrapper firstChildProps = {{ cheese: true }}>
        <Button disabled = {true}>Ok</Button>
      </Wrapper>
    </>
  );
};

Вот почти рабочая TS Playground.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
80
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам просто нужно добавить общее ограничение к WrapperProps<C> — то же самое, что есть у ComponentPropsWithoutRef<C> — то есть ElementType.

interface WrapperProps<C extends React.ElementType> {
  children: ReactNode;
  firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
Ответ принят как подходящий
  1. Вам необходимо ограничить параметр универсального типа C так, чтобы он соответствовал типу утилиты ComponentProps, например. с ElementType, как предложено в ответе СлавыСоболева:
interface WrapperProps<C extends React.ElementType> {
    children: ReactNode;
    firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
  1. Вы также можете сделать компонент Wrapper универсальным, но вам придется удалить React.FC, что не работает с другими дженериками:
const Wrapper = <C extends React.ElementType>({ children, firstChildProps }: WrapperProps<C>) => {
  // etc.
}

Затем вы можете использовать его для различного контента:

interface OtherProps {
    foo: "bar";
}

const Other: React.FC<OtherProps> = () => null;

{/* Fails type check because cheese does not exist on Button */}
<Wrapper<typeof Button> firstChildProps = {{ cheese: true }}>
    <Button disabled = {true}>Ok</Button>
</Wrapper>

{/* Error: Type 'number' is not assignable to type '"bar"'. */}
<Wrapper<typeof Other> firstChildProps = {{ foo: 0 }}>
    <Other foo = "bar" />
</Wrapper>

Ссылка на игровую площадку

Это фантастика! Именно то, что я искал. Я вижу ошибки, возникающие на игровой площадке, но не вижу никаких ошибок в моей IDE (появляются другие ошибки TS). Кажется, что он не читает <typeof Button> в строках JSX. Есть идеи?

bholtbholt 19.04.2024 07:53

Какая версия IDE и TS? TS <= 5.0.4, похоже, не поддерживает синтаксис <Wrapper<typeof Button> >tsplay.dev/mbrP4m

ghybs 19.04.2024 07:58

Ах, у меня это работает. У меня случайно был тип объединения firstChildProps: Partial<React.ComponentPropsWithoutRef<C>> | React.ReactNode, который переопределил ошибку.

bholtbholt 19.04.2024 08:03

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