Типизация frontmatter в TypeScript

Frontmatter — это специальный блок метаданных, который обычно располагается в начале Markdown-файла и ограничивается тремя тире ---. Он позволяет хранить структурированную информацию, такую как заголовок, автор, дата публикации, категории и теги. В экосистеме JavaScript и TypeScript frontmatter активно используется совместно с библиотеками Remark и Rehype для обработки Markdown и последующей трансформации в HTML.

Структура frontmatter

Frontmatter обычно записывается в формате YAML или JSON. Пример YAML-frontmatter:

---
title: "Пример статьи"
author: "Иван Иванов"
date: "2026-03-22"
tags:
  - JavaScript
  - TypeScript
  - Remark
---

В TypeScript важно заранее определить структуру этих данных, чтобы избежать ошибок при их использовании в коде.

Определение интерфейсов для frontmatter

Для строгой типизации необходимо описать интерфейс, который соответствует структуре frontmatter. Например:

interface Frontmatter {
  title: string;
  author: string;
  date: string; // Можно использовать тип Date при последующем парсинге
  tags: string[];
}

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

Парсинг frontmatter с Remark

Remark предоставляет плагины для работы с frontmatter. Один из популярных — remark-frontmatter. Он позволяет извлекать блоки frontmatter из Markdown-файлов. Пример использования:

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkFrontmatter from 'remark-frontmatter';
import yaml from 'js-yaml';

const markdown = `
---
title: "Пример статьи"
author: "Иван Иванов"
date: "2026-03-22"
tags:
  - JavaScript
  - TypeScript
  - Remark
---
# Заголовок статьи
`;

interface Frontmatter {
  title: string;
  author: string;
  date: string;
  tags: string[];
}

const processor = unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml']);

processor.process(markdown).then((file) => {
  const yamlNode = file.contents.match(/---\n([\s\S]+?)\n---/);
  if (yamlNode) {
    const data: Frontmatter = yaml.load(yamlNode[1]) as Frontmatter;
    console.log(data.title); // "Пример статьи"
  }
});

Здесь важно отметить:

  • remark-frontmatter только выделяет блок YAML, но не парсит его. Для этого используется библиотека js-yaml.
  • Типизация as Frontmatter гарантирует соответствие структуры TypeScript-интерфейсу.

Автоматическая типизация frontmatter через generics

Для проектов с большим количеством Markdown-файлов удобно создать универсальный утилитарный тип и функцию для загрузки frontmatter:

import fs from 'fs';
import path from 'path';
import yaml from 'js-yaml';

function loadFrontmatter<T>(filePath: string): T {
  const content = fs.readFileSync(path.resolve(filePath), 'utf-8');
  const match = content.match(/---\n([\s\S]+?)\n---/);
  if (!match) throw new Error('Frontmatter не найден');
  return yaml.load(match[1]) as T;
}

// Использование
interface PostFrontmatter {
  title: string;
  author: string;
  date: string;
  tags: string[];
}

const postData = loadFrontmatter<PostFrontmatter>('./posts/example.md');
console.log(postData.date);

Такой подход позволяет строго контролировать соответствие данных, избегая ошибок при дальнейшей обработке Markdown.

Интеграция с Rehype

После парсинга Markdown и извлечения frontmatter часто возникает необходимость трансформировать контент в HTML. Для этого используется Rehype:

import rehypeStringify from 'rehype-stringify';
import remark2rehype from 'remark-rehype';
import { unified } from 'unified';
import remarkParse from 'remark-parse';

const content = `
# Заголовок статьи
`;

unified()
  .use(remarkParse)
  .use(remark2rehype)
  .use(rehypeStringify)
  .process(content)
  .then((file) => {
    console.log(String(file));
  });

Типизация frontmatter позволяет отделить метаданные от контента и безопасно передавать их в шаблоны или API. Обычно frontmatter хранится отдельно от HTML-контента, но совместная типизация обеспечивает согласованность данных в проекте.

Полезные практики

  • Всегда определять интерфейс для frontmatter. Даже при небольшом проекте это предотвращает ошибки при изменении структуры.
  • Использовать отдельную функцию для извлечения и типизации frontmatter. Это упрощает повторное использование кода.
  • Проверять наличие обязательных полей (title, date) до использования. TypeScript не проверяет фактическое содержание YAML, поэтому runtime-валидация помогает избежать ошибок.
  • При работе с датами можно сразу преобразовывать строки в объекты Date для удобства сортировки и форматирования.

Типизация frontmatter в TypeScript вместе с Remark и Rehype позволяет создавать надежные и безопасные инструменты для генерации статического контента, автоматической документации и блог-платформ, минимизируя ошибки на этапе разработки.