Radix UI предоставляет низкоуровневые, полностью управляемые компоненты для React, которые легко интегрируются с любыми стилизующими решениями. Основная цель типизации в TypeScript — обеспечить строгую проверку свойств компонентов и их правильное использование в проекте, избегая ошибок во время компиляции.
Компоненты Radix UI, как правило, используют forwardRef для передачи рефа, что важно учитывать при типизации, особенно при создании оберток или кастомных компонентов.
import * as React from "react";
import * as Dialog from "@radix-ui/react-dialog";
const MyDialogTrigger = React.forwardRef<
HTMLButtonElement,
Dialog.DialogTriggerProps
>((props, ref) => <button ref={ref} {...props} />);
В этом примере тип Dialog.DialogTriggerProps
гарантирует, что все стандартные свойства для триггера диалога доступны,
а ref типизирован как HTMLButtonElement.
Radix UI использует собственные интерфейсы пропсов для каждого
компонента. Например, для Dialog:
DialogRootProps — пропсы корневого компонента
диалога.DialogTriggerProps — пропсы триггера открытия
диалога.DialogContentProps — пропсы контента диалога.Типизация позволяет безопасно расширять компоненты без потери автокомплита:
interface CustomDialogProps extends Dialog.DialogContentProps {
title: string;
footer?: React.ReactNode;
}
const CustomDialog = React.forwardRef<HTMLDivElement, CustomDialogProps>(
({ title, footer, ...props }, ref) => (
<Dialog.Content ref={ref} {...props}>
<h2>{title}</h2>
{props.children}
{footer && <footer>{footer}</footer>}
</Dialog.Content>
)
);
Здесь расширение интерфейса DialogContentProps добавляет
новые пропсы title и footer, сохраняя полную
совместимость с Radix UI.
Для создания обобщённых компонентов с Radix UI удобно использовать generic-типы. Например, если компонент должен работать с разными HTML-элементами:
interface ButtonProps<T extends React.ElementType> {
as?: T;
children: React.ReactNode;
}
const Button = <T extends React.ElementType = "button">({
as,
children,
...props
}: ButtonProps<T> & React.ComponentPropsWithoutRef<T>) => {
const Component = as || "button";
return <Component {...props}>{children}</Component>;
};
Такой подход позволяет использовать компонент как
<Button as="a" href="/home">, сохраняя типизацию для
всех стандартных HTML-пропсов.
Radix UI предоставляет типы для событий, чтобы избежать использования
any. Например, у DropdownMenu:
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
function handleSelect(event: DropdownMenu.DropdownMenuItemSelectEvent) {
console.log(event);
}
Тип DropdownMenuItemSelectEvent гарантирует наличие всех
полей события, специфичных для компонента.
Polymorphic ComponentsRadix UI допускает создание полиморфных компонентов через
asChild:
import * as Dialog from "@radix-ui/react-dialog";
<Dialog.Trigger asChild>
<button className="btn">Открыть диалог</button>
</Dialog.Trigger>
При этом DialogTriggerProps совместим с любым дочерним
элементом, и TypeScript корректно проверяет пропсы и рефы.
Для типизации таких случаев удобно использовать утилиты:
type PolymorphicRef<C extends React.ElementType> = React.ComponentPropsWithRef<C>["ref"];
type PolymorphicProps<C extends React.ElementType, Props = {}> = Props &
Omit<React.ComponentPropsWithoutRef<C>, keyof Props> & {
as?: C;
};
Эти утилиты позволяют создавать компоненты с as и
строгой типизацией пропсов, сохраняя совместимость с Radix UI.
Типизация Radix UI напрямую поддерживает передачу классов и стилей:
import * as Toast from "@radix-ui/react-toast";
const CustomToast = React.forwardRef<HTMLDivElement, Toast.ToastProps>(
({ className, ...props }, ref) => (
<Toast.Root ref={ref} className={`bg-gray-800 text-white p-4 ${className}`} {...props} />
)
);
ToastProps гарантируют правильность всех передаваемых
событий и состояний, а className можно расширять без потери
типизации.
forwardRef для оберток,
чтобы сохранить совместимость с внутренними рефами.interface или type, сохраняя автокомплит.any.Типизация в TypeScript для Radix UI — это инструмент не только для безопасности кода, но и для повышения читаемости, автокомплита и предсказуемости поведения компонентов в масштабных проектах.