Полиморфные компоненты со свойством 'as'
Типизируйте свойство 'as', чтобы Button отображался как или , сохраняя корректные типы свойств.
«Полиморфные компоненты со свойством 'as'» — бесплатный урок React Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения React Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс React Academy содержит 4 уроков всего.
Что такое полиморфный компонент?
Полиморфный компонент отрисовывается как разные HTML-элементы или компоненты в зависимости от свойства as — например, Button, который отрисовывается как <a> или <button>, сохраняя корректные типы TypeScript.
Простое свойство as (без TypeScript)
Базовая полиморфная реализация без обобщений — она теряет типобезопасность, но демонстрирует концепцию.
function Box({ as: Tag = 'div', children, ...props }) {
return <Tag {...props}>{children}</Tag>;
}
// Usage:
<Box as="section" className="hero">...</Box>
<Box as="article">...</Box>Типизированное свойство as с обобщениями
Добавьте параметр обобщённого типа, чтобы вывести правильные свойства для любого элемента, который отрисовывается через as.
type PolymorphicProps<E extends React.ElementType> = {
as?: E;
} & Omit<React.ComponentPropsWithoutRef<E>, 'as'>;
function Box<E extends React.ElementType = 'div'>({
as,
...props
}: PolymorphicProps<E>) {
const Tag = as ?? 'div';
return <Tag {...props} />;
}Поддержка ref
Добавьте поддержку forwardRef, сохранив полиморфную типизацию.
type PolymorphicRef<E extends React.ElementType> = React.ComponentPropsWithRef<E>['ref'];
type PolymorphicPropsWithRef<E extends React.ElementType, P = {}> = P &
Omit<React.ComponentPropsWithRef<E>, keyof P> & { as?: E };
const Button = React.forwardRef(
<E extends React.ElementType = 'button'>(
{ as, ...props }: PolymorphicPropsWithRef<E>,
ref: PolymorphicRef<E>
) => {
const Tag = as ?? 'button';
return <Tag ref={ref} {...props} />;
}
);Вывод типов TypeScript на практике
Когда вы задаёте as='a', TypeScript знает, что компонент принимает свойства, специфичные для ссылки, например href и target, но не свойства кнопки, например disabled.
// TypeScript allows href because as='a':
<Box as="a" href="https://example.com">Link</Box>
// TypeScript errors if you pass href to a div:
<Box as="div" href="...">Error!</Box> // TS: href is not a valid div propРасширение пользовательскими свойствами
Добавьте свойства, специфичные для компонента, к полиморфной основе, включив их в определение типа.
type TextProps<E extends React.ElementType> = PolymorphicProps<E> & {
size?: 'sm' | 'md' | 'lg';
weight?: 'normal' | 'bold';
};
function Text<E extends React.ElementType = 'p'>({
as,
size = 'md',
weight = 'normal',
className,
...props
}: TextProps<E>) {
const Tag = as ?? 'p';
return (
<Tag
className={[`text-${size}`, `font-${weight}`, className].filter(Boolean).join(' ')}
{...props}
/>
);
}Практический пример: ссылка, отображаемая как RouterLink
Компонент Button отображается как ссылка React Router, если передано свойство to, и как обычная кнопка в противном случае.
import { Link } from 'react-router-dom';
type ButtonProps =
| { to: string; href?: never } & React.ComponentPropsWithoutRef<typeof Link>
| { href: string; to?: never } & React.ComponentPropsWithoutRef<'a'>
| { to?: never; href?: never } & React.ComponentPropsWithoutRef<'button'>;
function Button({ to, href, ...props }: ButtonProps) {
if (to) return <Link to={to} {...props} />;
if (href) return <a href={href} {...props} />;
return <button type="button" {...props} />;
}Компонент Text дизайн-системы
Полиморфный компонент Text, по умолчанию отображаемый как <p>, но способный отображаться как h1–h6, span или label.
<Text as="h1" size="xl">Page Title</Text>
<Text as="span" weight="bold">Inline bold</Text>
<Text as="label" htmlFor="email">Email</Text> // htmlFor is valid because as='label'Ограничения для свойства as
Используйте extends React.ElementType, чтобы принимать как строки HTML-элементов ('div', 'a'), так и компоненты React.
function Card<E extends React.ElementType = 'div'>({ as, ...props }: PolymorphicProps<E>) {
const Tag = as ?? 'div';
return <Tag {...props} />;
}
// Works with HTML elements:
<Card as="section" aria-label="Products" />
// Works with React components:
<Card as={motion.div} animate={{ opacity: 1 }} />Как избежать распространённых ошибок
Не передавайте свойство as элементу вместе с остальными свойствами (это недопустимый атрибут HTML). Всегда извлекайте его перед передачей остальных свойств.
// Bad:
function Box({ as: Tag = 'div', ...props }) {
return <Tag as={...} {...props} />; // 'as' attribute on div is invalid HTML
}
// Good:
function Box({ as: Tag = 'div', ...props }) {
return <Tag {...props} />; // 'as' is used to pick Tag, not passed to element
}Особенности производительности
Полиморфные компоненты почти не создают накладных расходов: свойство as — всего лишь переменная, содержащая строку типа элемента или ссылку на компонент.
Быстрая проверка
Почему в полиморфном компоненте следует извлечь свойство as перед передачей остальных свойств элементу?
Итоги
Полиморфные компоненты используют обобщённое свойство as?: E extends React.ElementType, чтобы отрисовываться как любой элемент и при этом сохранять корректные свойства TypeScript. Извлекайте as перед передачей остальных свойств. Используйте React.ComponentPropsWithoutRef, чтобы включить в тип все атрибуты, специфичные для элемента.
Часто задаваемые вопросы
Урок «Полиморфные компоненты со свойством 'as'» бесплатный?
Да — полный текст урока «Полиморфные компоненты со свойством 'as'» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.
Чему я научусь в уроке «Полиморфные компоненты со свойством 'as'»?
Типизируйте свойство 'as', чтобы Button отображался как или , сохраняя корректные типы свойств. Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать React Academy?
Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Полиморфные компоненты со свойством 'as'»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке React Academy?
Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Размеченные объединения для вариантов компонентов
- Условные и отображаемые типы в React
- Полиморфные компоненты со свойством 'as'
- Типобезопасные формы и контракты ответов API