Frontend13 января 2025 г.8 мин чтенияКоманда Enextware

Создание безопасных и масштабируемых веб-приложений с TypeScript

Роль TypeScript в современной веб-разработке, а также его вклад в типобезопасность, качество кода и командную работу. Лучшие практики для проектов корпоративного уровня.

Объём услуги

Предложение и объём по теме «веб-разработка на TypeScript»

Это руководство поможет вам определиться. Перейдите на страницу соответствующего решения, чтобы узнать объём проекта, стартовый пакет, диапазон цен и порядок запроса предложения.

Смотреть объём
Создание безопасных и масштабируемых веб-приложений с TypeScript — обложка статьи

TypeScript стал незаменимым инструментом в современной веб-разработке. В этой статье мы подробно разберём, почему TypeScript так важен и как использовать его эффективно.

Что такое TypeScript и почему его стоит использовать?

Основные преимущества

1. Типобезопасность

Перехват ошибок времени выполнения на этапе компиляции
Меньше багов в продакшене
Умное автодополнение кода в IDE
Более безопасный рефакторинг

2. Качество кода

Самодокументируемый код
Более читаемая кодовая база
Меньше времени на код-ревью
Автоматическая генерация документации

3. Опыт разработчика

Поддержка IntelliSense
Предупреждения ещё до того, как ошибки будут пойманы
Более удобная навигация в больших проектах
Улучшенное взаимодействие в команде

Почему это важно

TypeScript — один из самых востребованных языков среди разработчиков
Его внедрение стабильно растёт год от года
Большая доля крупных компаний полагается на него
Он сокращает циклы исправления багов

Основы TypeScript

1. Базовые типы и интерфейсы

// Примитивные типы
let username: string = "Erdenay";
let age: number = 25;
let isActive: boolean = true;

// Массивы
let tags: string[] = ["web", "design", "seo"];
let scores: Array<number> = [95, 87, 92];

// Кортежи
let user: [string, number, boolean] = ["Erdenay", 25, true];

// Перечисления (enum)
enum UserRole {
  Admin = "ADMIN",
  User = "USER",
  Guest = "GUEST"
}

// Интерфейсы
interface Product {
  id: string;
  name: string;
  price: number;
  inStock: boolean;
  category?: string; // Необязательное
  readonly createdAt: Date; // Только для чтения
}

// Псевдонимы типов
type ID = string | number;
type Status = "pending" | "approved" | "rejected";

// Объединённые типы (union)
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(2));
  }
}

2. Продвинутые типы

// Дженерики
function getFirstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

const firstNumber = getFirstElement([1, 2, 3]); // number
const firstString = getFirstElement(["a", "b"]); // string

// Утилитарные типы (Utility Types)
interface User {
  id: string;
  name: string;
  email: string;
  age: number;
}

// Partial — делает все свойства необязательными
type PartialUser = Partial<User>;

// Pick — выбирает определённые свойства
type UserPreview = Pick<User, "id" | "name">;

// Omit — исключает определённые свойства
type UserWithoutEmail = Omit<User, "email">;

// Record — создаёт тип объекта с заданными ключами
type UserRoles = Record<string, UserRole>;

// Сопоставленные типы (Mapped Types)
type ReadonlyUser = {
  readonly [K in keyof User]: User[K];
};

// Условные типы (Conditional Types)
type IsString<T> = T extends string ? "yes" : "no";
type Test1 = IsString<string>; // "yes"
type Test2 = IsString<number>; // "no"

3. Типы функций

// Аннотации типов функций
function add(a: number, b: number): number {
  return a + b;
}

// Стрелочная функция с типами
const multiply = (a: number, b: number): number => a * b;

// Необязательные параметры и параметры по умолчанию
function greet(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`;
}

// Остаточные параметры (rest)
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, num) => acc + num, 0);
}

// Перегрузка функций
function parseValue(value: string): string[];
function parseValue(value: number): number[];
function parseValue(value: string | number): string[] | number[] {
  if (typeof value === "string") {
    return value.split(",");
  }
  return [value];
}

Лучшие практики TypeScript с React

1. Типы компонентов

import { FC, ReactNode } from 'react';

// Интерфейс пропсов
interface ButtonProps {
  children: ReactNode;
  onClick: () => void;
  variant?: "primary" | "secondary";
  disabled?: boolean;
}

// Функциональный компонент с типами
const Button: FC<ButtonProps> = ({
  children,
  onClick,
  variant = "primary",
  disabled = false
}) => {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn btn-${variant}`}
    >
      {children}
    </button>
  );
};

// Дженерик-компонент
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

2. Хуки с TypeScript

import { useState, useEffect, useCallback, useMemo } from 'react';

// useState с явным типом
const [user, setUser] = useState<User | null>(null);
const [count, setCount] = useState<number>(0);

// useEffect
useEffect(() => {
  const fetchUser = async () => {
    const response = await fetch('/api/user');
    const data: User = await response.json();
    setUser(data);
  };

  fetchUser();
}, []);

// useCallback
const handleClick = useCallback((id: string) => {
  console.log(`Clicked: ${id}`);
}, []);

// useMemo
const expensiveValue = useMemo(() => {
  return computeExpensiveValue(items);
}, [items]);

// Кастомный хук с типами
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}

3. Обработчики событий

import { ChangeEvent, FormEvent, MouseEvent } from 'react';

function Form() {
  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    // Обработка отправки формы
  };

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
    console.log("Button clicked");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" onChange={handleChange} />
      <button onClick={handleClick}>Submit</button>
    </form>
  );
}

TypeScript с Next.js

1. Компоненты страниц

// app/products/[id]/page.tsx
import type { Metadata } from 'next';

interface PageProps {
  params: {
    id: string;
  };
  searchParams: {
    [key: string]: string | string[] | undefined;
  };
}

export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const product = await fetchProduct(params.id);

  return {
    title: product.name,
    description: product.description,
  };
}

export default async function ProductPage({ params, searchParams }: PageProps) {
  const product = await fetchProduct(params.id);

  return <div>{product.name}</div>;
}

2. API-маршруты

// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server';

interface Product {
  id: string;
  name: string;
  price: number;
}

export async function GET(request: NextRequest) {
  const products: Product[] = await fetchProducts();

  return NextResponse.json(products);
}

export async function POST(request: NextRequest) {
  const body: Partial<Product> = await request.json();

  const newProduct = await createProduct(body);

  return NextResponse.json(newProduct, { status: 201 });
}

Корпоративные паттерны

1. Предметно-ориентированное проектирование (Domain-Driven Design)

// types/domain/product.ts
export interface ProductEntity {
  id: ProductId;
  name: ProductName;
  price: Money;
  stock: Stock;
}

export type ProductId = string & { readonly __brand: "ProductId" };
export type ProductName = string & { readonly __brand: "ProductName" };

export class Money {
  private constructor(
    public readonly amount: number,
    public readonly currency: string
  ) {}

  static create(amount: number, currency: string): Money {
    if (amount < 0) throw new Error("Amount cannot be negative");
    return new Money(amount, currency);
  }
}

2. Паттерн «Репозиторий»

interface Repository<T> {
  findById(id: string): Promise<T | null>;
  findAll(): Promise<T[]>;
  create(entity: Omit<T, 'id'>): Promise<T>;
  update(id: string, entity: Partial<T>): Promise<T>;
  delete(id: string): Promise<void>;
}

class ProductRepository implements Repository<Product> {
  async findById(id: string): Promise<Product | null> {
    // Реализация
  }

  // ... другие методы
}

Производительность и оптимизация

1. Настройка строгого режима (Strict Mode)

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "moduleResolution": "bundler",
    "target": "ES2022",
    "lib": ["ES2022", "DOM"],
    "jsx": "preserve",
    "incremental": true
  }
}

2. Оптимизация сборки

Время компиляции:

Использование ссылок на проекты (project references) для ускорения сборок
Инкрементальная компиляция для более быстрых пересборок
Параллелизация проверки типов

Размер бандла:

Устранение неиспользуемого кода с помощью tree shaking
Импорты только типов: `import type { User } from './types'`
Нулевые накладные расходы во время выполнения (типы компилируются в ничто)

Командная работа

1. Преимущества для код-ревью

Ошибки типов перехватываются до выполнения, что снижает количество багов
Самодокументируемый код сокращает время ревью
Стандартизированные паттерны ускоряют адаптацию новых разработчиков

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

/**
 * Вычисляет общую стоимость товаров в корзине
 * @param items - Массив товаров корзины
 * @param discountCode - Необязательный промокод
 * @returns Итоговая стоимость после применения скидок
 * @throws {Error} Если промокод недействителен
 * @example
 * ```ts
 * const total = calculateTotal(cartItems, "SUMMER25");
 * ```
 */
function calculateTotal(
  items: CartItem[],
  discountCode?: string
): number {
  // Реализация
}

Стандарты TypeScript в Enextware

Мы используем TypeScript во всех наших проектах:

100% покрытие типами
Включён строгий режим
Конфигурация ESLint + Prettier
Автоматизированное тестирование с Jest
Проверка типов в CI/CD

Результаты:

Заметно меньше багов в продакшене
Более быстрые циклы разработки
Более высокое общее качество кода

Контакт

Для профессиональных веб-приложений, созданных с помощью TypeScript:

Телефон: +90 536 628 0007

Эл. почта: info@enextware.com

Похожие статьи

Все статьи

Создадим современный фронтенд с упором на производительность

Разрабатываем быстрые, управляемые интерфейсы с сильным UX на масштабируемой архитектуре Next.js.