Компонент React со свойством «as», которое указывает на другой компонент и также наследует его свойства

Я хочу создать MyComponent со свойством «as», которое позволит ему наследовать свойства компонента as, чтобы я мог получать типизацию TypeScript.

например

Использование с элементами HTML:

<MyComponent 
  as = "label" 
  htmlFor = "caption" /* this should autocomplete from label */
  asdf = "" /* this should give error */
  className = "test"
>
  Test
</MyComponent>

С компонентами React:

<MyComponent 
  as = {MyGrid} 
  rows = {4}  /* this should autocomplete from MyGrid */ 
  asdf = "" /* this should give error */

>
  Test
</MyComponent>

Пока у меня есть это, которое работает как компонент, но, к сожалению, не наследует типы.

import { useRef, forwardRef } from "react";
import type {
  PropsWithChildren,
  ElementType,
  ComponentProps,
  ForwardedRef,
  ReactElement,
} from "react";


type MyProps<T extends ElementType> = 
  ComponentProps<T> &
  PropsWithChildren & 
  { as?: T };

function _MyComponent<T extends ElementType = "div">(
  { as, children, ...props }: MyProps<T>,
  ref: ForwardedRef<HTMLElement>,
): ReactElement<T> {
  
  const Comp = as ?? "div";

  return (
    <Comp {...props} ref = {ref}>
      {children}
    </Comp>
  );
}

const MyComponent = forwardRef(_MyComponent);
export default MyComponent;

Как мне нужно изменить Typescript, чтобы я мог получать предложения по типу?

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

Ответы 1

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

Я заработал, добавив утверждение типа в вызов frontRef:

export const MyComponent = forwardRef(_MyComponent) as <
  T extends ElementType = "div"
>(
  props: MyProps<T> & { ref?: React.Ref<Element> }
) => ReactElement | null;

Использование

<MyComponent 
  as = "label" 
  htmlFor = "caption" // this autocompletes 
  asdf = "" // this gives error
>
  Test
</MyComponent>
const MyGrid = ({ rows }: { rows: number }) => (
  <div style = {{ gridTemplateRows: `repeat(${rows}, 1fr)` }}></div>
);

<MyComponent as = {MyGrid} rows = {4}>Test</MyComponent>;

Редактировать

Модифицируем MyProps, чтобы он работал для элементов HTML.

type MyProps<T extends ElementType> = {
  as?: T;
} & ComponentPropsWithoutRef<T>;

Кажется, он работает для MyGrid, но не для элементов HTML, таких как «метка».

Kernel James 30.08.2024 07:23

Пожалуйста, смотрите обновленный ответ @KernelJames

docker compose 30.08.2024 07:28

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