$ sudo teach IT

Модуль 12.5

Roadmap: куда расти дальше

Карта развития TypeScript-разработчика

Введение: что дальше

Поздравляю! Вы прошли базовый курс TypeScript и освоили основные концепции. Но это только начало пути. TypeScript — это не просто язык, это экосистема с сотнями инструментов, фреймворков и направлений.

В этом уроке мы составим карту развития — roadmap, который поможет вам выбрать направление и понять, что изучать дальше. Не пытайтесь освоить всё сразу — выберите одно направление и углубляйтесь в него.

1. Фронтенд разработка с TypeScript

1.1 React + TypeScript

React — самый популярный UI фреймворк, а TypeScript делает его ещё мощнее. Изучение React + TypeScript — отличный выбор для карьеры.

// Пример: Типизированный React компонент
import React, { useState, useEffect, useCallback } from 'react';

interface User {
  id: number;
  name: string;
  email: string;
}

interface UserCardProps {
  userId: number;
  onSelect: (user: User) => void;
}

export const UserCard: React.FC<UserCardProps> = ({ userId, onSelect }) => {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState<boolean>(true);

  const fetchUser = useCallback(async () => {
    try {
      const response = await fetch(`/api/users/${userId}`);
      const data: User = await response.json();
      setUser(data);
    } catch (error) {
      console.error('Failed to fetch user:', error);
    } finally {
      setLoading(false);
    }
  }, [userId]);

  useEffect(() => {
    void fetchUser();
  }, [fetchUser]);

  if (loading) return <div className="loading">Loading...</div>;
  if (!user) return <div className="error">User not found</div>;

  return (
    <div className="user-card" onClick={() => onSelect(user)}>
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
};
Что изучать:
  • React hooks с типами
  • Context API + TypeScript
  • React Router с типизацией
  • React Query / SWR
  • Formуправление: React Hook Form
Инструменты:
  • Create React App + TypeScript
  • Vite + React + TypeScript
  • Storybook с TypeScript
  • React Testing Library
Время обучения:

2-3 месяца для базового уровня

1.2 Vue + TypeScript

// Пример: Vue 3 Composition API с TypeScript
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';

interface User {
  id: number;
  name: string;
  email: string;
}

const users = ref<User[]>([]);
const searchQuery = ref<string>('');
const loading = ref<boolean>(true);

const filteredUsers = computed(() =>
  users.value.filter(user =>
    user.name.toLowerCase().includes(searchQuery.value.toLowerCase())
  )
);

async function fetchUsers(): Promise<void> {
  try {
    const response = await fetch('/api/users');
    users.value = await response.json();
  } finally {
    loading.value = false;
  }
}

onMounted(() => {
  void fetchUsers();
});
</script>

<template>
  <div>
    <input v-model="searchQuery" placeholder="Search..." />
    <div v-if="loading">Loading...</div>
    <ul v-else>
      <li v-for="user in filteredUsers" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  </div>
</template>

1.3 Angular

Angular использует TypeScript «из коробки» и обеспечивает полную типизацию. Это отличный выбор для enterprise-проектов.

// Пример: Angular компонент с TypeScript
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { UserService } from '../services/user.service';
import { User } from '../models/user.model';

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngIf="loading">Loading...</div>
    <ul *ngIf="!loading">
      <li *ngFor="let user of users"
          (click)="selectUser(user)">
        {{ user.name }}
      </li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  @Input() searchTerm: string = '';
  @Output() userSelected = new EventEmitter<User>();

  users: User[] = [];
  loading: boolean = true;

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    this.userService.getUsers().subscribe({
      next: (users) => {
        this.users = users;
        this.loading = false;
      },
      error: (err) => {
        console.error('Error:', err);
        this.loading = false;
      }
    });
  }

  selectUser(user: User): void {
    this.userSelected.emit(user);
  }
}

2. Бэкенд разработка с TypeScript

2.1 Node.js + Express/Fastify

// Пример: Express сервер с TypeScript
import express, { Request, Response, NextFunction } from 'express';

interface User {
  id: number;
  name: string;
  email: string;
}

interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
}

const app = express();
app.use(express.json());

// Middleware для логирования
app.use((req: Request, res: Response, next: NextFunction) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

// GET /api/users
app.get('/api/users', async (req: Request, res: Response) => {
  try {
    const users: User[] = await UserService.getAll();
    const response: ApiResponse<User[]> = {
      success: true,
      data: users,
    };
    res.json(response);
  } catch (error) {
    const response: ApiResponse<never> = {
      success: false,
      error: 'Failed to fetch users',
    };
    res.status(500).json(response);
  }
});

// GET /api/users/:id
app.get('/api/users/:id', async (req: Request, res: Response) => {
  const id = parseInt(req.params.id, 10);

  if (isNaN(id)) {
    res.status(400).json({ success: false, error: 'Invalid ID' });
    return;
  }

  const user = await UserService.getById(id);
  if (!user) {
    res.status(404).json({ success: false, error: 'User not found' });
    return;
  }

  res.json({ success: true, data: user });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

2.2 NestJS

NestJS — это фреймворк для создания масштабируемых серверных приложений, вдохновлённый Angular. Он использует TypeScript из коробки и предоставляет архитектуру на основе модулей.

// Пример: NestJS модуль с TypeScript
import { Controller, Get, Post, Body, Param, Inject } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './entities/user.entity';

@Controller('users')
export class UsersController {
  constructor(
    @Inject(UsersService)
    private readonly usersService: UsersService,
  ) {}

  @Get()
  async findAll(): Promise<User[]> {
    return this.usersService.findAll();
  }

  @Get(':id')
  async findOne(@Param('id') id: string): Promise<User> {
    return this.usersService.findOne(id);
  }

  @Post()
  async create(@Body() createUserDto: CreateUserDto): Promise<User> {
    return this.usersService.create(createUserDto);
  }
}

// DTO (Data Transfer Object)
export class CreateUserDto {
  name: string;
  email: string;
  password: string;
}

2.3 Deno и Bun

Deno

  • TypeScript из коробки
  • Встроенная безопасность
  • Нативная поддержка Web API
  • Совместимость с Node.js (частично)
  • Простой деплой

Bun

  • TypeScript из коробки
  • В 10-100 раз быстрее Node.js
  • Встроенный тестовый раннер
  • Встроенный пакетный менеджер
  • Совместимость с Node.js API

3. Full-stack разработка

3.1 Next.js + TypeScript

// Пример: Next.js страница с TypeScript
// pages/users/[id].tsx
import { GetServerSideProps, NextPage } from 'next';
import { useRouter } from 'next/router';
import { User } from '../../types';

interface UserPageProps {
  user: User | null;
  error?: string;
}

const UserPage: NextPage<UserPageProps> = ({ user, error }) => {
  const router = useRouter();
  const { id } = router.query;

  if (error) {
    return <div>Error: {error}</div>;
  }

  if (!user) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      <button onClick={() => router.back()}>Go Back</button>
    </div>
  );
};

export const getServerSideProps: GetServerSideProps<UserPageProps> = async (context) => {
  const { id } = context.params!;

  try {
    const response = await fetch(`http://localhost:3000/api/users/${id}`);

    if (!response.ok) {
      return { props: { user: null, error: 'User not found' } };
    }

    const user: User = await response.json();
    return { props: { user } };
  } catch {
    return { props: { user: null, error: 'Failed to fetch user' } };
  }
};

export default UserPage;

3.2 Remix

// Пример: Remix страница с TypeScript
// app/routes/users/$id.tsx
import { LoaderFunctionArgs, json } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';

interface User {
  id: string;
  name: string;
  email: string;
}

export async function loader({ params }: LoaderFunctionArgs): Promise<Response> {
  const { id } = params;

  if (!id) {
    throw new Response('Not Found', { status: 404 });
  }

  const response = await fetch(`http://localhost:3000/api/users/${id}`);

  if (!response.ok) {
    throw new Response('Not Found', { status: 404 });
  }

  const user: User = await response.json();
  return json({ user });
}

export default function UserPage() {
  const { user } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

3.3 SvelteKit

// Пример: SvelteKit страница
// src/routes/users/[id]/+page.server.ts
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';

interface User {
  id: string;
  name: string;
  email: string;
}

export const load: PageServerLoad = async ({ params }) => {
  const { id } = params;

  const response = await fetch(`http://localhost:3000/api/users/${id}`);

  if (!response.ok) {
    error(404, 'User not found');
  }

  const user: User = await response.json();
  return { user };
};

// src/routes/users/[id]/+page.svelte
<script lang="ts">
  import type { PageData } from './$types';
  export let data: PageData;
</script>

<h1>{data.user.name}</h1>
<p>{data.user.email}</p>

4. Мобильная разработка

4.1 React Native + TypeScript

// Пример: React Native компонент с TypeScript
import React, { useState } from 'react';
import { View, Text, TextInput, Button, FlatList, StyleSheet } from 'react-native';

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

export const TodoApp: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [input, setInput] = useState<string>('');

  const addTodo = () => {
    if (input.trim()) {
      const newTodo: Todo = {
        id: Date.now().toString(),
        text: input.trim(),
        completed: false,
      };
      setTodos([...todos, newTodo]);
      setInput('');
    }
  };

  const toggleTodo = (id: string) => {
    setTodos(
      todos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  const renderTodo = ({ item }: { item: Todo }) => (
    <View style={styles.todoItem}>
      <Text
        style={[styles.todoText, item.completed && styles.completed]}
        onPress={() => toggleTodo(item.id)}>
        {item.text}
      </Text>
    </View>
  );

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        value={input}
        onChangeText={setInput}
        placeholder="Add todo..."
      />
      <Button title="Add" onPress={addTodo} />
      <FlatList
        data={todos}
        renderItem={renderTodo}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20 },
  input: { borderWidth: 1, borderColor: '#ccc', padding: 10, marginBottom: 10 },
  todoItem: { padding: 15, borderBottomWidth: 1, borderBottomColor: '#eee' },
  todoText: { fontSize: 16 },
  completed: { textDecorationLine: 'line-through', color: '#999' },
});

Альтернативы:

  • Expo — платформа для React Native с упрощённой настройкой
  • React Native Paper — UI библиотека с Material Design
  • React Navigation — навигация для React Native
  • Flutter — альтернатива (язык Dart, похож на TypeScript)

5. DevOps и инфраструктура

// Пример: Docker + TypeScript
// Dockerfile
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["node", "dist/index.js"]

// docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

6. Типизация баз данных

6.1 Prisma

// schema.prisma — Схема Prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  role      Role     @default(USER)
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

enum Role {
  USER
  ADMIN
  MODERATOR
}

// Использование в коде
import { PrismaClient, User, Post } from '@prisma/client';

const prisma = new PrismaClient();

async function createUser(name: string, email: string): Promise<User> {
  return prisma.user.create({
    data: { name, email },
  });
}

async function getUserWithPosts(userId: number): Promise<User & { posts: Post[] }> {
  return prisma.user.findUniqueOrThrow({
    where: { id: userId },
    include: { posts: true },
  });
}

async function createPost(authorId: number, title: string): Promise<Post> {
  return prisma.post.create({
    data: {
      title,
      author: { connect: { id: authorId } },
    },
  });
}

6.2 TypeORM и Drizzle

TypeORM

  • Decorator-based ORM
  • Поддержка Active Record и Data Mapper
  • Миграции
  • Сложные запросы
  • TypeScript-first

Drizzle ORM

  • Легковесный ORM
  • SQL-like API
  • Высокая производительность
  • Type-safe запросы
  • Минимальный runtime

7. Тестирование как карьера

// Пример: E2E тесты с Playwright
import { test, expect, Page } from '@playwright/test';

test.describe('User Management', () => {
  let page: Page;

  test.beforeEach(async ({ browser }) => {
    page = await browser.newPage();
  });

  test('should display user list', async () => {
    await page.goto('/users');

    const userCards = page.locator('[data-testid="user-card"]');
    await expect(userCards).toHaveCount(5);

    const firstUserName = userCards.first().locator('h3');
    await expect(firstUserName).toBeVisible();
  });

  test('should create new user', async () => {
    await page.goto('/users/new');

    await page.fill('[data-testid="name-input"]', 'Alice');
    await page.fill('[data-testid="email-input"]', 'alice@example.com');
    await page.click('[data-testid="submit-button"]');

    await expect(page).toHaveURL(/\/users\/\d+/);
    await expect(page.locator('h1')).toContainText('Alice');
  });

  test('should handle validation errors', async () => {
    await page.goto('/users/new');

    await page.click('[data-testid="submit-button"]');

    const error = page.locator('[data-testid="error-message"]');
    await expect(error).toBeVisible();
    await expect(error).toContainText('Name is required');
  });
});

// Пример: API тесты
import request from 'supertest';
import app from '../src/app';

test('GET /api/users should return users', async () => {
  const response = await request(app)
    .get('/api/users')
    .expect(200);

  expect(response.body.success).toBe(true);
  expect(Array.isArray(response.body.data)).toBe(true);
});

test('POST /api/users should create user', async () => {
  const response = await request(app)
    .post('/api/users')
    .send({ name: 'Alice', email: 'alice@example.com' })
    .expect(201);

  expect(response.body.data.name).toBe('Alice');
});

8. Архитектурные паттерны

// 1. Clean Architecture с TypeScript
// domain/entities/User.ts
export interface User {
  id: string;
  name: string;
  email: string;
}

// domain/repositories/UserRepository.ts
export interface UserRepository {
  findById(id: string): Promise<User | null>;
  findByEmail(email: string): Promise<User | null>;
  save(user: User): Promise<void>;
}

// domain/use-cases/CreateUser.ts
export class CreateUserUseCase {
  constructor(private userRepository: UserRepository) {}

  async execute(name: string, email: string): Promise<User> {
    const existing = await this.userRepository.findByEmail(email);
    if (existing) {
      throw new Error('User already exists');
    }

    const user: User = {
      id: crypto.randomUUID(),
      name,
      email,
    };

    await this.userRepository.save(user);
    return user;
  }
}

// infrastructure/repositories/PostgresUserRepository.ts
import { UserRepository } from '../../domain/repositories/UserRepository';
import { User } from '../../domain/entities/User';

export class PostgresUserRepository implements UserRepository {
  async findById(id: string): Promise<User | null> {
    // Реализация с PostgreSQL
    const result = await db.query('SELECT * FROM users WHERE id = $1', [id]);
    return result.rows[0] || null;
  }

  async findByEmail(email: string): Promise<User | null> {
    const result = await db.query('SELECT * FROM users WHERE email = $1', [email]);
    return result.rows[0] || null;
  }

  async save(user: User): Promise<void> {
    await db.query(
      'INSERT INTO users (id, name, email) VALUES ($1, $2, $3)',
      [user.id, user.name, user.email]
    );
  }
}

// 2. Microservices Architecture
// shared/types/Event.ts
export interface DomainEvent {
  type: string;
  timestamp: Date;
  payload: Record<string, unknown>;
}

// services/user-service/src/events/UserCreated.ts
export interface UserCreatedEvent extends DomainEvent {
  type: 'USER_CREATED';
  payload: {
    userId: string;
    name: string;
    email: string;
  };
}

// services/order-service/src/handlers/UserCreatedHandler.ts
export function handleUserCreated(event: UserCreatedEvent) {
  // Обработка события
}

9. Открытый исходный код

Почему стоит участвовать в Open Source:

  • Портфолио — реальные проекты для демонстрации навыков
  • Нетворкинг — общение с опытными разработчиками
  • Обучение — чтение чужого кода лучший способ учиться
  • Карьера — мейнтейнеры часто получают предложения о работе
  • Вклад в сообщество — помогает другим разработчикам
// Как начать:
// 1. Найдите проект, который используете
// 2. Изучите CONTRIBUTING.md
// 3. Начните с "good first issue" меток
// 4. Fork → Branch → PR

// Пример: Contributions к TypeScript экосистеме
// - Исправление опечаток в документации
// - Добавление примеров кода
// - Исправление багов (issues)
// - Написание тестов
// - Улучшение документации

10. Сертификации и обучение

Сертификации

  • Microsoft: TypeScript Certification
  • AWS: Developer Associate
  • Google: Cloud Developer
  • Meta: Frontend Developer

Платформы обучения

  • Frontend Masters
  • Udemy
  • Coursera
  • Pluralsight
  • Official TypeScript Handbook

11. Портфолио и карьера

Проекты для портфолио:

  1. TODO приложение — CRUD операции, фильтрация, сортировка
  2. Блог платформа — авторизация, посты, комментарии
  3. API клиент — работа с REST/GraphQL API
  4. Чат приложение — WebSocket, реальное время
  5. E-commerce — корзина, оплата, заказы
  6. Dashboard — графики, аналитика, таблицы
// Структура проекта для портфолио:
project/
├── src/
│   ├── domain/           # Business logic
│   ├── application/      # Use cases
│   ├── infrastructure/   # External integrations
│   ├── presentation/     # UI layer
│   └── shared/           # Shared utilities
├── tests/
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── docs/
│   ├── architecture.md
│   └── api.md
├── .github/
│   └── workflows/
│       └── ci.yml
├── tsconfig.json
├── jest.config.ts
├── eslint.config.js
├── .prettierrc
└── README.md

12. Ресурсы для обучения

Документация

  • TypeScript Handbook
  • TypeScript Deep Dive
  • MDN Web Docs
  • React/Vue/Angular Docs

Практика

  • LeetCode / HackerRank
  • Exercism (TypeScript track)
  • Type Challenges
  • Build your own X
Книги:
  • "TypeScript in Depth" — Kirill Konchenko
  • "Effective TypeScript" — Dan Vanderkam
  • "Learning TypeScript" — Ethan Niser
  • "TypeScript Design Patterns" — Vilcov

13. Визуальные библиотеки и UI фреймворки

13.1 Библиотеки компонентов

// Пример: Типизированный компонент с Material UI
import React from 'react';
import { TextField, Button, Box, Typography } from '@mui/material';

interface LoginFormProps {
  onSubmit: (email: string, password: string) => void;
  loading?: boolean;
}

export const LoginForm: React.FC<LoginFormProps> = ({ onSubmit, loading = false }) => {
  const [email, setEmail] = React.useState<string>('');
  const [password, setPassword] = React.useState<string>('');
  const [errors, setErrors] = React.useState<Record<string, string>>({});

  const validate = (): boolean => {
    const newErrors: Record<string, string> = {};

    if (!email) {
      newErrors.email = 'Email is required';
    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      newErrors.email = 'Invalid email format';
    }

    if (!password) {
      newErrors.password = 'Password is required';
    } else if (password.length < 6) {
      newErrors.password = 'Password must be at least 6 characters';
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (validate()) {
      onSubmit(email, password);
    }
  };

  return (
    <Box component="form" onSubmit={handleSubmit} sx={{ maxWidth: 400, mx: 'auto' }}>
      <Typography variant="h4" gutterBottom>Login</Typography>

      <TextField
        fullWidth
        label="Email"
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        error={Boolean(errors.email)}
        helperText={errors.email}
        margin="normal"
      />

      <TextField
        fullWidth
        label="Password"
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        error={Boolean(errors.password)}
        helperText={errors.password}
        margin="normal"
      />

      <Button
        fullWidth
        type="submit"
        variant="contained"
        disabled={loading}
        sx={{ mt: 3 }}
      >
        {loading ? 'Logging in...' : 'Login'}
      </Button>
    </Box>
  );
};
React библиотеки:
  • Material UI (MUI)
  • Ant Design
  • Chakra UI
  • RADIX UI
  • Shadcn/UI
Vue библиотеки:
  • Vuetify
  • Element Plus
  • PrimeVue
  • Naive UI
  • Quasar

13.2 Tailwind CSS с TypeScript

// Tailwind CSS с TypeScript
// tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#3b82f6',
          900: '#1e3a8a',
        },
      },
    },
  },
  plugins: [],
};

export default config;

// Типизированный компонент с Tailwind
interface ButtonProps {
  variant: 'primary' | 'secondary' | 'danger';
  size: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({
  variant,
  size,
  children,
  onClick,
  disabled = false,
}) => {
  const variantClasses = {
    primary: 'bg-blue-500 hover:bg-blue-600 text-white',
    secondary: 'bg-gray-500 hover:bg-gray-600 text-white',
    danger: 'bg-red-500 hover:bg-red-600 text-white',
  };

  const sizeClasses = {
    sm: 'px-3 py-1 text-sm',
    md: 'px-4 py-2 text-base',
    lg: 'px-6 py-3 text-lg',
  };

  return (
    <button
      className={`${variantClasses[variant]} ${sizeClasses[size]} rounded font-medium transition-colors`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};

14. Управление состоянием (State Management)

// 1. Redux Toolkit с TypeScript
import { createSlice, configureStore, PayloadAction } from '@reduxjs/toolkit';

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

interface TodoState {
  items: Todo[];
  filter: 'all' | 'active' | 'completed';
}

const initialState: TodoState = {
  items: [],
  filter: 'all',
};

const todoSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    addTodo: (state, action: PayloadAction<string>) => {
      state.items.push({
        id: crypto.randomUUID(),
        text: action.payload,
        completed: false,
      });
    },
    toggleTodo: (state, action: PayloadAction<string>) => {
      const todo = state.items.find(t => t.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    },
    setFilter: (state, action: PayloadAction<TodoState['filter']>) => {
      state.filter = action.payload;
    },
  },
});

export const { addTodo, toggleTodo, setFilter } = todoSlice.actions;

// 2. Zustand (лёгкий state management)
import create from 'zustand';

interface BearState {
  bears: number;
  increase: (by: number) => void;
}

const useBearStore = create<BearState>((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
}));

// 3. Jotai (атомарный state management)
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);
const doubledAtom = atom((get) => get(countAtom) * 2);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      {count}
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  );
}

15. Работа с формами

// React Hook Form с TypeScript
import { useForm, SubmitHandler, FieldErrors } from 'react-hook-form';

interface FormData {
  firstName: string;
  lastName: string;
  email: string;
  age: number;
  password: string;
}

export const UserProfileForm: React.FC = () => {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<FormData>({
    defaultValues: {
      firstName: '',
      lastName: '',
      email: '',
      age: 0,
      password: '',
    },
  });

  const onSubmit: SubmitHandler<FormData> = async (data) => {
    console.log(data);
    // API call
  };

  const onError = (errors: FieldErrors<FormData>) => {
    console.log('Form errors:', errors);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit, onError)}>
      <input
        {...register('firstName', { required: 'First name is required' })}
      />
      {errors.firstName && <span>{errors.firstName.message}</span>}

      <input
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: 'Invalid email',
          },
        })}
      />
      {errors.email && <span>{errors.email.message}</span>}

      <input
        type="number"
        {...register('age', {
          required: 'Age is required',
          min: { value: 18, message: 'Must be at least 18' },
          max: { value: 120, message: 'Must be at most 120' },
        })}
      />
      {errors.age && <span>{errors.age.message}</span>}

      <input
        type="password"
        {...register('password', {
          required: 'Password is required',
          minLength: { value: 8, message: 'Min 8 characters' },
        })}
      />
      {errors.password && <span>{errors.password.message}</span>}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
};

План действий

Выберите одно направление и следуйте ему:

  1. Неделя 1-2: Выберите направление (фронтенд/бэкенд/full-stack)
  2. Неделя 3-4: Изучите основы фреймворка
  3. Месяц 2: Создайте мини-проект
  4. Месяц 3: Добавьте тесты и документацию
  5. Месяц 4: Задеплойте проект
  6. Месяц 5: Внесите вклад в Open Source
  7. Месяц 6: Обновите портфолио и LinkedIn

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

Roadmap: куда расти дальше

10 вопросов

Roadmap: куда двигаться дальше

Premium