Типы для провайдеров

В экосистеме MDX ключевую роль играют провайдеры, которые позволяют интегрировать React-компоненты и контент Markdown с расширениями JSX. Типизация провайдеров критична для обеспечения безопасного и предсказуемого взаимодействия компонентов внутри MDX-документов.

Основной интерфейс провайдера

MDX предоставляет MDXProvider, который принимает объект components — словарь компонентов, используемых для замены стандартных HTML-тегов. Типизация этого объекта выполняется через интерфейсы:

import { MDXProviderComponents } from '@mdx-js/react';

const components: MDXProviderComponents = {
  h1: (props) => <h1 style={{ color: 'red' }} {...props} />,
  p: (props) => <p style={{ fontSize: 16 }} {...props} />,
};

MDXProviderComponents позволяет строго указать соответствие тега HTML и компонента React, обеспечивая проверку типов на этапе компиляции. Это исключает ошибки при попытке передать неподдерживаемый компонент для стандартного тега.

Кастомизация типов через Generics

MDX поддерживает расширение типов с помощью обобщений. Если требуется использовать собственные теги или расширенные свойства, можно определить собственный интерфейс:

import { MDXProviderComponents } from '@mdx-js/react';

interface CustomComponents {
  MyButton: React.FC<{ label: string; onClick: () => void }>;
}

const customComponents: MDXProviderComponents & CustomComponents = {
  h1: (props) => <h1 {...props} />,
  MyButton: ({ label, onClick }) => <button onCl ick={onClick}>{label}</button>,
};

Использование Generics гарантирует, что любая ошибка в передаваемых пропсах будет выявлена на этапе компиляции TypeScript, что особенно важно для больших MDX-проектов с множеством кастомных компонентов.

MDXContext и типы провайдеров

Внутри MDX используется контекст MDXContext, который позволяет компонентам получать доступ к текущему набору компонентов:

import { useMDXComponents, MDXProvider } from '@mdx-js/react';

const components = {
  h2: (props) => <h2 style={{ fontWeight: 'bold' }} {...props} />,
};

const Component = () => {
  const allComponents = useMDXComponents();
  const H2 = allComponents.h2;
  return <H2>Заголовок второго уровня</H2>;
};

<MDXProvider components={components}>
  <Component />
</MDXProvider>

Функция useMDXComponents возвращает типизированный объект компонентов, что позволяет безопасно использовать их внутри вложенных компонентов. Типизация здесь особенно важна при интеграции с TypeScript, так как она предотвращает обращение к несуществующим компонентам или неверные пропсы.

Динамическая типизация компонентов

MDX позволяет заменять компоненты динамически, что удобно при создании тем или скинов для документации:

const defaultComponents = {
  p: (props) => <p style={{ color: 'black' }} {...props} />,
};

const darkModeComponents = {
  p: (props) => <p style={{ color: 'white' }} {...props} />,
};

const currentTheme = 'dark';
<MDXProvider components={currentTheme === 'dark' ? darkModeComponents : defaultComponents}>
  <MDXContent />
</MDXProvider>

Для безопасного переключения тем полезно определить объединённый тип:

type ThemeComponents = typeof defaultComponents & typeof darkModeComponents;

Это гарантирует, что любая комбинация тем соответствует строго типизированному контракту компонентов.

Типизация MDXContent при использовании кастомных провайдеров

MDX-файлы при компиляции становятся компонентами React с типами, автоматически генерируемыми TypeScript. Чтобы расширить их функциональность, можно явно указать пропсы:

import { MDXContent } from './example.mdx';

interface MDXProps {
  components?: MDXProviderComponents & { MyButton?: React.FC<{ label: string }> };
}

<MDXContent components={{ MyButton: ({ label }) => <button>{label}</button> }} />;

Такое определение позволяет использовать собственные компоненты внутри MDX без потери типизации и с полной поддержкой автодополнения.

Использование интерфейса MDXComponents для строгого контроля

Для проектов с большим количеством MDX-документов рекомендуется создать отдельный интерфейс всех компонентов:

interface MDXComponents {
  h1: React.FC<React.HTMLAttributes<HTMLHeadingElement>>;
  h2: React.FC<React.HTMLAttributes<HTMLHeadingElement>>;
  p: React.FC<React.HTMLAttributes<HTMLParagraphElement>>;
  MyButton: React.FC<{ label: string; onClick: () => void }>;
}

const components: MDXComponents = { ... };

Такой подход обеспечивает:

  • Единообразие компонентов во всех MDX-документах.
  • Автодополнение и проверку типов в редакторах кода.
  • Предотвращение ошибок при передаче пропсов, особенно для пользовательских компонентов.

Эта методика позволяет использовать MDX максимально безопасно и гибко, обеспечивая строгую типизацию всех компонентов, что особенно важно для масштабируемых проектов, где документы и React-компоненты активно взаимодействуют.