Библиотеки Remark и Rehype, построенные на основе экосистемы unified, используют систему типизации TypeScript для обеспечения строгой совместимости между различными плагинами и деревьями синтаксиса. Generic типы в unified позволяют точно определить структуру входных и выходных данных, а также специфицировать типы узлов (nodes) AST (Abstract Syntax Tree), обеспечивая безопасность при трансформациях.
Процессор в unified создается через функцию unified(),
которая возвращает объект с методами .use(),
.parse(), .stringify(), .run(),
.runSync(). Типы этих методов зависят от generic-параметров
процессора:
import { unified } from 'unified';
import { Node } from 'unist';
type MyNode = Node & { myProp: string };
const processor = unified<MyNode>();
Здесь MyNode задаёт тип AST, с которым
будет работать процессор. Все последующие плагины будут ожидать именно
этот тип, что предотвращает ошибки при обращении к нестандартным
свойствам узлов.
Unified поддерживает до трёх generic-параметров:
unified<
ParseTree = Node, // Тип дерева после парсинга
Input = string, // Входной формат
Output = Node, // Тип дерева после трансформаций
StringOutput = string // Тип финального результата stringify
>()
.parse()..process()..run()..stringify().Использование этих параметров обеспечивает строгую типизацию, которая помогает выявлять ошибки на этапе компиляции.
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import { Node } from 'unist';
interface MyMarkdownNode extends Node {
type: 'paragraph' | 'text';
value?: string;
}
const processor = unified<
MyMarkdownNode, // ParseTree
string, // Input
MyMarkdownNode, // Output
string // StringOutput
>()
.use(remarkParse)
.use(remarkStringify);
const markdown = 'Пример текста';
const processed = processor.processSync(markdown).toString();
В этом примере тип MyMarkdownNode применяется ко всем
этапам обработки: парсинг, трансформация и генерация строки. Ошибки
типа, например, обращение к несуществующему свойству value
на узле другого типа, будут обнаружены на этапе компиляции.
Плагины в unified также могут быть типизированы с помощью generic-параметров:
import { Transformer } from 'unified';
import { Node } from 'unist';
const myPlugin: Transformer<MyMarkdownNode, MyMarkdownNode> = (tree) => {
tree.children.forEach((node) => {
if (node.type === 'text' && node.value) {
node.value = node.value.toUpperCase();
}
});
return tree;
};
Здесь Transformer<ParseTree, OutputTree>
гарантирует, что плагин будет принимать дерево одного типа и возвращать
дерево того же типа или совместимого типа. Это особенно важно при
построении цепочек из нескольких плагинов.
В unified AST строится на основе стандарта unist,
где каждый узел имеет обязательное поле type и массив
children для вложенных узлов. Generic типы позволяют
уточнять свойства узлов:
interface ParagraphNode extends Node {
type: 'paragraph';
children: TextNode[];
}
interface TextNode extends Node {
type: 'text';
value: string;
}
type MyAST = ParagraphNode | TextNode;
Использование union-типов для узлов AST обеспечивает автодополнение и проверки типов при обходе дерева:
const processor = unified<MyAST>();
const tree = processor.parse('Текст') as MyAST;
tree.children.forEach((node) => {
if (node.type === 'text') {
console.log(node.value.toUpperCase());
}
});
Rehype использует тот же API, что и Remark, но с деревом HTML
(hast). Generic типы позволяют определить структуру
HTML-узлов:
import { unified } from 'unified';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';
import { Element, Root } from 'hast';
interface MyElement extends Element {
tagName: 'p' | 'div';
}
type MyHAST = Root & { children: MyElement[] };
const processor = unified<MyHAST, string, MyHAST, string>()
.use(rehypeParse)
.use(rehypeStringify);
Тип MyHAST задаёт строгую структуру HTML-дерева, что
позволяет безопасно обходить и модифицировать элементы без runtime
ошибок.
unified<ParseTree, Input, Output, StringOutput>
задаёт типы процессора.Transformer<ParseTree, OutputTree> для типобезопасных
трансформаций.Generic типы являются фундаментом при построении сложных пайплайнов Remark и Rehype, обеспечивая одновременно гибкость и безопасность.