Перейти к содержимому

26. TypeScript в Astro

Astro поддерживает TypeScript «из коробки» — не нужно устанавливать дополнительные пакеты или настраивать Babel. Весь TypeScript компилируется при сборке, а в режиме разработки вы получаете полноценное автодополнение и проверку типов.

Astro генерирует оптимальный tsconfig.json при создании проекта. Рекомендуется использовать один из официальных пресетов:

{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@layouts/*": ["src/layouts/*"],
"@utils/*": ["src/utils/*"]
}
}
}

Пресеты строгости: base, strict, strictest. Для новых проектов рекомендуется strict — он включает все полезные проверки без излишней строгости.

Ключевая конвенция Astro: экспортируйте интерфейс Props прямо в frontmatter компонента. Astro автоматически его подхватывает:

src/components/Card.astro
---
export interface Props {
title: string;
description: string;
image?: string; // опциональный
variant: 'primary' | 'secondary'; // union type
tags: string[];
meta?: {
author: string;
date: Date;
};
}
const {
title,
description,
image,
variant = 'primary',
tags,
meta,
} = Astro.props;
---
<article class={`card card--${variant}`}>
{image && <img src={image} alt={title} />}
<h2>{title}</h2>
<p>{description}</p>
{meta && <span>{meta.author} · {meta.date.toLocaleDateString('ru')}</span>}
<div class="tags">
{tags.map(tag => <span class="tag">{tag}</span>)}
</div>
</article>

При использовании компонента TypeScript будет проверять все пропсы:

<!-- Ошибка: variant — обязательный, теги должны быть массивом -->
<Card title="Привет" description="Текст" variant="primary" tags={['astro', 'ts']} />

Функция getStaticPaths возвращает пути для статической генерации. Используйте тип GetStaticPaths для корректной типизации:

---
import type { GetStaticPaths } from 'astro';
import { getCollection } from 'astro:content';
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
};
// Типизация пропсов страницы
interface Props {
post: Awaited<ReturnType<typeof getCollection<'blog'>>>[number];
}
const { post } = Astro.props;
---

При работе с Content Collections используйте тип CollectionEntry:

---
import type { CollectionEntry } from 'astro:content';
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
const { title, description, publishedAt } = post.data;
---

Тип CollectionEntry<'blog'> автоматически выводится из вашей схемы коллекции, поэтому post.data будет полностью типизирован.

Пространство имён astro предоставляет дополнительные утилитарные типы:

import type {
AstroComponentFactory, // Тип Astro-компонента
HTMLAttributes, // HTML атрибуты с aria- и data-
MarkdownInstance, // Импортированный .md файл
ImageMetadata, // Импортированное изображение
AstroGlobal, // Тип Astro (глобальный объект)
} from 'astro';
// Расширение HTML атрибутов
interface ButtonProps extends HTMLAttributes<'button'> {
variant: 'primary' | 'danger';
loading?: boolean;
}
// Передача компонента как пропса
interface LayoutProps {
header: AstroComponentFactory;
footer: AstroComponentFactory;
}
src/pages/api/posts.ts
import type { APIRoute } from 'astro';
interface PostBody {
title: string;
content: string;
}
export const POST: APIRoute = async ({ request }) => {
const body = await request.json() as PostBody;
if (!body.title || !body.content) {
return new Response(JSON.stringify({ error: 'Missing fields' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
};