Mastering TypeScript 5.x: Advanced Patterns for Full-Stack Developers

#TypeScript#FullStack#Advanced Patterns

TL;DR:
TypeScript 5.x предлагает мощные инструменты для создания устойчивых и масштабируемых приложений. В этой статье мы рассмотрим продвинутые паттерны, такие как Generics с ограничениями, Mapped Types, Template Literal Types и Conditional Types с использованием infer, которые помогут вам улучшить код и снизить количество runtime-ошибок.

Введение

TypeScript давно перестал быть просто “JavaScript с типами”. Сегодня это мощная система типов, которая позволяет выявлять сложные архитектурные ошибки еще на этапе разработки. Для full-stack разработчиков, работающих с React и Node.js, освоение продвинутых возможностей TypeScript — это не только способ избежать any, но и возможность создавать самодокументируемые и масштабируемые кодовые базы. В этой статье мы рассмотрим паттерны, которые я ежедневно использую в production-приложениях.

Generics с ограничениями

Generics позволяют создавать переиспользуемые компоненты, которые работают с различными типами, сохраняя при этом безопасность типов. Однако настоящая магия начинается, когда вы добавляете ограничения.

interface HasId {
  id: string | number;
}

function getRecordById<T extends HasId>(records: T[], id: string | number): T | undefined {
  return records.find(record => record.id === id);
}

Расширяя HasId, мы гарантируем, что любой тип, переданный в getRecordById, должен иметь свойство id. Это позволяет писать более безопасную логику для работы с данными.

Mapped Types и Key Remapping

Mapped Types позволяют создавать новые типы на основе существующих. Начиная с TypeScript 4.1+, появилась возможность Key Remapping, которая особенно полезна для создания API-оберток или менеджеров состояния.

type FormState = {
  userName: string;
  email: string;
};

type FormValidation = {
  [K in keyof FormState as `validate${Capitalize<K>}`]: (value: FormState[K]) => boolean;
};

// Результат:
// {
//   validateUserName: (value: string) => boolean;
//   validateEmail: (value: string) => boolean;
// }

Template Literal Types

Template Literal Types позволяют использовать интерполяцию строк в системе типов. Они идеально подходят для соблюдения соглашений по именованию или паттернов маршрутизации.

type Color = 'primary' | 'secondary';
type Intensity = 'low' | 'high';

type ThemeClass = `bg-${Color}-${Intensity}`;

const myButton: ThemeClass = 'bg-primary-high'; // Valid
const brokenButton: ThemeClass = 'bg-red-low';   // Error!

Conditional Types и infer

Conditional Types работают как тернарные операторы для типов. В сочетании с ключевым словом infer они позволяют “извлекать” типы из сложных структур, таких как возвращаемый тип функции или Promise.

type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function fetchUser() {
  return { id: 1, name: "Arslan" };
}

type User = GetReturnType<typeof fetchUser>; // { id: number, name: string }

Практическое применение

Использование продвинутых паттернов TypeScript не делает ваш код сложнее — оно переносит нагрузку проверок с runtime на этап компиляции. Это позволяет сократить количество unit-тестов, улучшить опыт разработчиков и создавать более предсказуемые системы.

Заключение

TypeScript 5.x предлагает мощные инструменты для улучшения качества и масштабируемости вашего кода. Используя Generics с ограничениями, Mapped Types, Template Literal Types и Conditional Types с infer, вы можете создавать более устойчивые и предсказуемые приложения. Какой из этих паттернов ваш любимый? Делитесь в комментариях!


Источник: https://dev.to/iammuhammadarslan/mastering-typescript-5x-advanced-patterns-for-full-stack-developers-39jj