PurpleSchool — курсы программирования онлайн
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развития
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    TypeScript дженерики на практике: полный гайд для разработчиков

    TypeScript дженерики на практике: полный гайд для разработчиков

    Аватар автора TypeScript дженерики на практике: полный гайд для разработчиков

    Антон Ларичев

    Иконка календаря30 мая 2026
    typescriptgenericsjavascriptfrontendmiddleИконка уровня middle
    Картинка поста TypeScript дженерики на практике: полный гайд для разработчиков

    Введение

    TypeScript дженерики (generics) — один из самых мощных инструментов системы типов. Они позволяют писать переиспользуемый код, который работает с разными типами данных, сохраняя при этом строгую типизацию. Если функции и классы без дженериков жёстко привязаны к конкретным типам, то обобщённые конструкции принимают тип как параметр и возвращают результат, точно соответствующий входным данным.

    В этом гайде разберём синтаксис, ограничения (constraints), условные типы, утилитные дженерики и практические примеры, которые встречаются в реальной работе с React, Node.js и библиотеками вроде Axios или Prisma.

    Базовый синтаксис дженериков

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

    // Функция принимает значение типа T и возвращает то же T
    function identity<T>(value: T): T {
      return value;
    }
    
    // TypeScript сам выведет тип: number
    const num = identity(42);
    
    // Тип можно указать явно
    const str = identity<string>("hello");
    

    Дженерики можно использовать в интерфейсах, типах и классах:

    // Обобщённый интерфейс ответа API
    interface ApiResponse<T> {
      data: T;
      status: number;
      error: string | null;
    }
    
    // Конкретизация под пользователя
    type UserResponse = ApiResponse<{ id: number; name: string }>;
    

    Ограничения через extends

    Иногда нужно гарантировать, что тип-параметр обладает определённой структурой. Для этого применяется ключевое слово extends.

    // T должен иметь поле length
    function logLength<T extends { length: number }>(value: T): T {
      console.log(value.length);
      return value;
    }
    
    logLength("строка"); // ок, у строки есть length
    logLength([1, 2, 3]); // ок, у массива есть length
    // logLength(42); // ошибка: у number нет length
    

    Ещё полезный паттерн — получение типа ключа объекта через keyof:

    // K должен быть одним из ключей объекта T
    function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
      return obj[key];
    }
    
    const user = { id: 1, name: "Anna", role: "admin" };
    const name = getProperty(user, "name"); // тип string
    // getProperty(user, "email"); // ошибка: нет такого ключа
    

    Значения по умолчанию

    Для параметров типа можно задать значение по умолчанию — как для обычных аргументов функций. Это упрощает работу с обобщёнными компонентами.

    // По умолчанию состояние — пустой объект
    interface State<T = Record<string, unknown>> {
      loading: boolean;
      data: T;
    }
    
    // Без явного указания типа
    const initial: State = { loading: false, data: {} };
    

    Условные типы и infer

    Условные типы выглядят как тернарный оператор, но работают на уровне типов. Они особенно полезны в сочетании с infer — для извлечения вложенных типов.

    // Извлекаем тип элемента массива
    type ElementOf<T> = T extends Array<infer U> ? U : never;
    
    type Numbers = ElementOf<number[]>; // number
    type Mixed = ElementOf<(string | boolean)[]>; // string | boolean
    

    Типичный сценарий — получение типа возвращаемого значения функции (это встроенный ReturnType):

    // Реализация ReturnType вручную
    type MyReturnType<T extends (...args: any[]) => any> = 
      T extends (...args: any[]) => infer R ? R : never;
    
    function fetchUser() {
      return { id: 1, name: "Bob" };
    }
    
    type User = MyReturnType<typeof fetchUser>; // { id: number; name: string }
    

    Утилитные дженерики

    TypeScript предоставляет набор встроенных обобщённых типов, которые покрывают большинство задач трансформации.

    interface Article {
      id: number;
      title: string;
      body: string;
      publishedAt: Date;
    }
    
    // Все поля становятся необязательными
    type ArticleDraft = Partial<Article>;
    
    // Только нужные поля
    type ArticlePreview = Pick<Article, "id" | "title">;
    
    // Все поля кроме указанных
    type ArticleWithoutDate = Omit<Article, "publishedAt">;
    
    // Делает все поля обязательными
    type StrictArticle = Required<Article>;
    

    Практический пример: типобезопасный fetch

    Соберём небольшую обёртку над fetch, которая возвращает данные нужного типа без приведения через as.

    // Обобщённая функция запроса
    async function apiGet<T>(url: string): Promise<T> {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      return response.json() as Promise<T>;
    }
    
    // Использование с указанием формы ответа
    interface Post {
      id: number;
      title: string;
    }
    
    const posts = await apiGet<Post[]>("/api/posts");
    posts[0].title; // автодополнение и проверка типов
    

    Дженерики в React-компонентах

    Обобщённые компоненты помогают строить переиспользуемые списки, таблицы и селекты без потери типизации элементов.

    // Список с произвольным типом элементов
    interface ListProps<T> {
      items: T[];
      renderItem: (item: T) => React.ReactNode;
    }
    
    function List<T>({ items, renderItem }: ListProps<T>) {
      return <ul>{items.map((item, i) => <li key={i}>{renderItem(item)}</li>)}</ul>;
    }
    
    // item автоматически имеет тип { id: number; name: string }
    <List
      items={[{ id: 1, name: "Anna" }]}
      renderItem={(item) => item.name}
    />;
    

    Частые ошибки

    Чрезмерное обобщение. Не каждой функции нужны дженерики. Если параметр типа используется только один раз и не влияет на возврат — это просто шум. Заменяйте таким случаем обычным типом.

    Использование any вместо ограничений. Конструкция <T extends any> или T = any обнуляет пользу от обобщения. Применяйте unknown, если действительно не знаете тип, или задавайте осмысленные ограничения через extends.

    Игнорирование вывода типов. TypeScript умеет выводить параметры из аргументов. Явное указание identity<string>("x") избыточно, если компилятор сам справляется. Это засоряет код.

    Путаница с keyof и значением ключа. Тип T[K] — это тип значения, а не строки ключа. Помните, что ключ остаётся литералом, а через индексный доступ вы получаете именно поле.

    Дженерик вместо union. Если функция принимает строго два варианта типа, перегрузки или union читаются понятнее, чем обобщение.

    Заключение

    Дженерики превращают TypeScript из строгого, но негибкого языка в выразительный инструмент моделирования данных. Они позволяют описать связь между входом и выходом функции, между состоянием и его трансформациями, между ключом объекта и типом значения. Начните с простых случаев — обёрток над API, утилит для коллекций, компонентов-списков — и постепенно подключайте условные типы и infer, когда библиотечный код требует точного описания форм данных. Главное правило: дженерик должен делать код безопаснее и читаемее, а не наоборот.

    Иконка глаза8

    Комментарии

    0

    Постройте личный план изучения JavaScript Advanced - продвинутые концепции языка и ООП до уровня Middle — бесплатно!

    JavaScript Advanced - продвинутые концепции языка и ООП — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    TypeScript с нуля

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    React и Redux Toolkit

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    React Native и Expo Router

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.7
    3 999 ₽ 6 990 ₽
    Подробнее

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

    Картинка поста Webpack vs Vite в 2025: что выбрать для нового проекта
    Иконка аватараАнтон
    Иконка календаря23 мая 2026
    webpackvitefrontend+ 2middleИконка уровня middle

    Webpack vs Vite в 2025: что выбрать для нового проекта

    Сравнение Webpack и Vite в 2025 году: скорость сборки, конфигурация, HMR, экосистема. Что выбрать для нового проекта и почему.

    Иконка чипа0
    Иконка глаза131
    Иконка комментариев0
    Картинка поста Как стать frontend-разработчиком в 2025: дорожная карта
    Иконка аватараАнтон
    Иконка календаря10 мая 2026
    frontendкарьераjavascript+ 2juniorИконка уровня junior

    Как стать frontend-разработчиком в 2025: дорожная карта

    Дорожная карта frontend-разработчика 2025: какие технологии учить, в каком порядке и как собрать портфолио для первой работы.

    Иконка чипа0
    Иконка глаза305
    Иконка комментариев1
    Картинка поста Микрофронтенды и Module Federation: когда применять и как внедрить
    Иконка аватараАнтон
    Иконка календаря28 мая 2026
    микрофронтендыmodule federationwebpack+ 2seniorИконка уровня senior

    Микрофронтенды и Module Federation: когда применять и как внедрить

    Микрофронтенды и Module Federation: разбираем когда нужна такая архитектура, как настроить host и remote приложения и избежать типичных ошибок.

    Иконка чипа0
    Иконка глаза78
    Иконка комментариев0
    Иконка чипа0