Inference типов

MDX сочетает возможности Markdown и JSX, позволяя создавать динамические документы с компонентами React. Одним из ключевых аспектов работы с MDX в TypeScript или современных JavaScript-проектах является инференс типов. Понимание того, как типы выводятся автоматически и как их можно корректно настроить, позволяет избежать ошибок на этапе компиляции и улучшить интеграцию с React-компонентами.

Основы типизации MDX

MDX-файлы компилируются в React-компоненты. При импорте MDX-файла через TypeScript или с использованием @mdx-js/react создаётся компонент, тип которого по умолчанию определяется как React.FC<MDXContentProps>. Интерфейс MDXContentProps предоставляет возможность передавать props в MDX-документ, что особенно важно для динамических данных и управления контентом через параметры.

import { MDXProvider } from '@mdx-js/react';
import MyDocument from './document.mdx';

type MDXProps = {
  title: string;
  showHeader: boolean;
};

<MyDocument title="Документ" showHeader={true} />

В этом примере TypeScript выводит типы автоматически: title и showHeader ожидаются в качестве свойств компонента, если они объявлены в интерфейсе MDXContentProps. Если типы не указаны явно, TypeScript может вывести их как any, что снижает безопасность типов.

Инференс через JSX внутри MDX

MDX позволяет использовать JSX-компоненты прямо внутри Markdown. При этом типы props для таких компонентов также подлежат инференсу.

import { Button } from './Button';

<Button label="Отправить" onCl ick={() => console.log('clicked')} />

Если компонент Button типизирован как:

type ButtonProps = {
  label: string;
  onClick: () => void;
};

export const Button: React.FC<ButtonProps> = ({ label, onClick }) => (
  <button onCl ick={onClick}>{label}</button>
);

TypeScript автоматически выводит типы для использования внутри MDX. Любая ошибка, например передача onCl ick="строка", будет поймана на этапе компиляции.

Инференс типов для components в MDXProvider

MDXProvider позволяет переопределять рендеринг стандартных Markdown-тегов, таких как h1, p и a. Типы этих компонентов также могут быть выведены автоматически:

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

const components = {
  h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
    <h1 style={{ color: 'red' }} {...props} />
  ),
  a: (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
    <a target="_blank" {...props} />
  ),
};

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

TypeScript понимает, что каждый компонент должен соответствовать типу props соответствующего HTML-элемента. Это позволяет использовать все стандартные атрибуты без дополнительного указания типов, сохраняя гибкость и безопасность.

Автоматическая генерация типов для MDX-файлов

Для крупных проектов полезно использовать генерацию типов для MDX-файлов. С помощью @types/mdx или пакета mdx-types можно создать декларации:

declare module '*.mdx' {
  import { ComponentType } from 'react';
  const MDXComponent: ComponentType<Record<string, any>>;
  export default MDXComponent;
}

Это позволяет TypeScript автоматически выводить типы при импорте любого MDX-файла. Для более строгой типизации можно заменить Record<string, any> на конкретный интерфейс props, поддерживающий статический контроль типов.

Инференс типов и MDXProvider с Generic

В последних версиях MDX можно использовать Generic для передачи типа props, что улучшает автодополнение и проверку типов:

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

type DocumentProps = { title: string; date: string };

const MyDocument: MDXContent<DocumentProps> = ({ title, date }) => (
  <>
    <h1>{title}</h1>
    <p>{date}</p>
  </>
);

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

Частые ошибки при инференсе

  1. Отсутствие декларации типов для MDX-файлов — TypeScript воспринимает их как any, теряется безопасность типов.
  2. Несоответствие props компонентов внутри MDX — если компонент ожидает определённые свойства, передача неверных типов приводит к ошибкам компиляции.
  3. Несогласованность типов при использовании MDXProvider — компоненты должны строго соответствовать HTML-атрибутам или интерфейсу props.

Рекомендации по улучшению типизации

  • Всегда использовать интерфейсы или типы для props MDX-компонентов.
  • Для проектов с большим количеством MDX-файлов применить генерацию деклараций модулей.
  • Использовать Generic для MDXContent, чтобы сделать инференс строго типизированным.
  • Проверять соответствие типов для компонентов, переопределяемых через MDXProvider.

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