Type guards — это специальные проверки типов, позволяющие точно определить тип значения во время выполнения. В контексте JavaScript и MDX это особенно важно, так как MDX объединяет Markdown и JSX, где данные могут иметь динамический и разнородный характер. Type guards позволяют безопасно работать с такими данными, избегая ошибок типов и некорректного рендеринга компонентов.
Type guard — это функция или конструкция, которая сужает тип переменной. С помощью type guards можно гарантировать, что определённая ветка кода будет работать только с конкретным типом.
function formatValue(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
Ключевой момент: typeof позволяет
сужать примитивные типы (string, number,
boolean, symbol, undefined).
class Article {
constructor(public title: string) {}
}
class Video {
constructor(public duration: number) {}
}
function renderContent(content: Article | Video) {
if (content instanceof Article) {
return `<h1>${content.title}</h1>`;
} else {
return `<p>Duration: ${content.duration} minutes</p>`;
}
}
Особенность: instanceof проверяет
принадлежность объекта к определённому классу и подходит для сложных
объектов.
TypeScript позволяет создавать собственные функции-проверки с
возвращаемым типом value is Type, что делает их
полноценными type guards.
interface Image {
url: string;
alt: string;
}
interface Text {
content: string;
}
function isImage(node: Image | Text): node is Image {
return (node as Image).url !== undefined;
}
function renderNode(node: Image | Text) {
if (isImage(node)) {
return `<img src="${node.url}" alt="${node.alt}" />`;
} else {
return `<p>${node.content}</p>`;
}
}
Преимущество: Пользовательский type guard можно использовать для любых сложных структур данных, включая MDX-узлы и компоненты.
MDX позволяет вставлять JSX в Markdown. При работе с MDX необходимо часто проверять типы узлов, особенно при динамическом рендеринге компонентов или при трансформации контента.
import { Node } from 'unist';
import { visit } from 'unist-util-visit';
interface HeadingNode extends Node {
type: 'heading';
depth: number;
children: Node[];
}
function isHeading(node: Node): node is HeadingNode {
return node.type === 'heading';
}
function collectHeadings(tree: Node) {
const headings: HeadingNode[] = [];
visit(tree, (node) => {
if (isHeading(node)) {
headings.push(node);
}
});
return headings;
}
Суть: С помощью type guard isHeading
можно безопасно работать с узлами AST MDX, не боясь, что доступ к
depth или children вызовет ошибку.
Type guards можно комбинировать для более сложных условий, что часто встречается при обработке динамических компонентов в MDX.
interface ButtonProps {
label: string;
onClick?: () => void;
}
interface LinkProps {
href: string;
text: string;
}
type ComponentProps = ButtonProps | LinkProps;
function isButton(props: ComponentProps): props is ButtonProps {
return (props as ButtonProps).label !== undefined;
}
function renderComponent(props: ComponentProps) {
if (isButton(props)) {
return `<button>${props.label}</button>`;
} else {
return `<a href="${props.href}">${props.text}</a>`;
}
}
Преимущество: Позволяет безопасно рендерить компоненты с разной структурой данных без runtime ошибок.
MDX-компоненты часто получают props динамически, поэтому type guards полезны для условного рендеринга внутри JSX:
import { ComponentProps } from './types';
export function DynamicRenderer(props: ComponentProps) {
if ('label' in props) {
return <button>{props.label}</button>;
}
return <a href={props.href}>{props.text}</a>;
}
Важно: Проверка через in является
упрощённой формой type guard, которая хорошо подходит для небольших
интерфейсов.
typeof, instanceof,
in, пользовательские функции
value is Type.Type guards — фундаментальный инструмент для безопасной работы с динамическими структурами данных, повышающий стабильность и предсказуемость MDX-приложений.