Разработка фронтенда сайта на TypeScript
TypeScript — это не просто «JavaScript с типами». Это инструмент, который делает рефакторинг предсказуемым, документирует контракты между модулями и ловит целый класс ошибок на этапе компиляции. Для проектов с командой от двух человек или сроком жизни больше года TypeScript — не опция, а базовое требование.
Мы используем TypeScript как основной язык для всего фронтенда: от конфигурации Vite до типизации API-ответов.
Конфигурация для строгого TypeScript
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"exactOptionalPropertyTypes": true,
"paths": {
"@/*": ["./src/*"]
}
}
}
strict: true включает 8 флагов разом. noUncheckedIndexedAccess добавляет undefined к типу при доступе к массиву по индексу — половина runtime-ошибок исчезает сама по себе.
Типизация API
Типы для API-ответов — один из первых шагов на любом проекте. Ручное написание утомительно и устаревает вместе с бэкендом. Правильный подход:
OpenAPI → автогенерация типов:
npx openapi-typescript https://api.example.com/openapi.json -o src/types/api.ts
Результат — точные типы для всех endpoint'ов. Затем типобезопасный клиент:
import createClient from 'openapi-fetch';
import type { paths } from '@/types/api';
const client = createClient<paths>({ baseUrl: 'https://api.example.com' });
// TypeScript знает тип параметров и ответа
const { data, error } = await client.GET('/products/{id}', {
params: { path: { id: '42' } }
});
if (data) {
console.log(data.name); // string — без as, без any
}
Паттерны для масштабируемого фронтенда
Discriminated union для состояний загрузки:
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; message: string };
function renderState<T>(state: AsyncState<T>): string {
switch (state.status) {
case 'idle': return 'Нажмите для загрузки';
case 'loading': return 'Загрузка...';
case 'success': return `Загружено: ${JSON.stringify(state.data)}`;
case 'error': return `Ошибка: ${state.message}`;
}
}
Компилятор проверяет exhaustiveness — если добавить новый статус, все switch-выражения без него станут ошибкой.
Branded types для предотвращения перепутывания ID:
type UserId = string & { readonly __brand: 'UserId' };
type ProductId = string & { readonly __brand: 'ProductId' };
function toUserId(id: string): UserId { return id as UserId; }
function getUser(id: UserId): Promise<User> { ... }
const productId: ProductId = toProductId('abc');
getUser(productId); // Ошибка компиляции: ProductId не совместим с UserId
Zod для валидации runtime-данных:
import { z } from 'zod';
const ProductSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(255),
price: z.number().positive(),
category: z.enum(['electronics', 'clothing', 'books']),
tags: z.array(z.string()).default([]),
});
type Product = z.infer<typeof ProductSchema>; // Тип выведен автоматически
// Валидация ответа API
const raw = await fetch('/api/products/42').then(r => r.json());
const product = ProductSchema.parse(raw); // Throws если данные невалидны
Настройка линтинга
// .eslintrc или eslint.config.js
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/strict-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked"
],
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-floating-promises": "error"
}
}
no-floating-promises ловит незавершённые promise-цепочки — целый класс ошибок, которые иначе молча проглатываются.
Интеграция с фреймворками
TypeScript работает с любым фронтенд-стеком:
| Фреймворк | Уровень поддержки |
|---|---|
| React 18 | Полная, JSX через tsx |
| Vue 3 | Полная, SFC через <script setup lang="ts"> |
| Svelte 4/5 | Через lang="ts" в script-блоке |
| Solid.js | Первоклассная поддержка |
| Vanilla (без фреймворка) | Полная |
Сроки
- Неделя 1: настройка tsconfig, ESLint, генерация типов из OpenAPI/GraphQL схем
- Недели 2–3: разработка компонентов и бизнес-логики с полной типизацией
-
Неделя 4: code review на наличие
any, рефакторинг слабо типизированных мест, тесты с Vitest
Проект сдаётся с нулевыми any в продакшн-коде и включённым strict: true. Исключения документируются явным комментарием.







