Что такое generics в TypeScript?

JuniorTypeScript · Frontend·Обновлено 23 июня 2026
Коротко
Generics — это механизм обобщённого программирования, позволяющий создавать компоненты (функции, классы, интерфейсы), которые работают с разными типами данных, сохраняя при этом строгую типизацию. Тип передаётся как параметр в угловых скобках и фиксируется при вызове или инстанцировании.

Generics в TypeScript

Generics (обобщения) позволяют писать переиспользуемый код, который остаётся типобезопасным независимо от конкретного типа данных. Вместо того чтобы дублировать логику для number, string, User и других типов — или терять типизацию через any — вы описываете алгоритм один раз, а конкретный тип подставляется позже.

Базовый синтаксис

Параметр типа обозначается буквой в угловых скобках (чаще всего T, K, V, U) и ведёт себя как переменная, но на уровне системы типов.

function identity<T>(value: T): T {
  return value;
}

identity<string>('hello'); // тип явно указан
identity(42);              // тип выведен автоматически: number

Зачем это нужно

Без generics приходится выбирать между потерей типизации (any) и дублированием кода:

// Плохо — теряем информацию о типе
function wrapAny(value: any): any {
  return { data: value };
}

// Хорошо — тип сохраняется
function wrap<T>(value: T): { data: T } {
  return { data: value };
}

const result = wrap('hello'); // { data: string }

Generic-интерфейсы и типы

interface ApiResponse<T> {
  data: T;
  status: number;
  error?: string;
}

type UserResponse = ApiResponse<User>;
type ListResponse = ApiResponse<User[]>;

Generic-классы

class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

const stack = new Stack<number>();
stack.push(1);
stack.push(2);

Ограничения типов (constraints)

С помощью extends можно ограничить допустимые типы:

interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(value: T): T {
  console.log(value.length); // TypeScript знает, что length существует
  return value;
}

logLength('текст');   // ok
logLength([1, 2, 3]); // ok
logLength(42);        // ошибка: number не имеет length

Несколько параметров типа

function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

const merged = merge({ name: 'Alice' }, { age: 30 });
// Тип: { name: string } & { age: number }

Keyof и generics

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: 'Bob' };
getProperty(user, 'name'); // string
getProperty(user, 'id');   // number
// getProperty(user, 'email'); // ошибка компиляции

Generics по умолчанию

interface Box<T = string> {
  value: T;
}

const box: Box = { value: 'привет' }; // T = string по умолчанию

Когда использовать generics

  • Утилитарные функции (сортировка, фильтрация, маппинг)
  • Обёртки над HTTP-ответами
  • Структуры данных (стек, очередь, дерево)
  • React-компоненты с типизированными пропсами

Что хочет услышать интервьюер

Объяснение проблемы, которую решают generics: переиспользование кода без потери типизации

Знание синтаксиса: параметр типа в угловых скобках, вывод типа компилятором

Понимание разницы между generics и any

Знание constraints через extends

Примеры применения: функции, интерфейсы, классы

Пример: Базовая generic-функция

function identity<T>(value: T): T {
  return value;
}

const str = identity('hello'); // тип: string
const num = identity(42);      // тип: number

Пример: Generic-интерфейс для API

interface ApiResponse<T> {
  data: T;
  status: number;
  error?: string;
}

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

async function fetchUser(id: number): Promise<ApiResponse<User>> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

const response = await fetchUser(1);
console.log(response.data.name); // TypeScript знает, что это string

Пример: Constraints — ограничение типа

interface HasId {
  id: number;
}

// T должен иметь поле id
function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find(item => item.id === id);
}

const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
const user = findById(users, 1); // тип: { id: number; name: string } | undefined

Типичные ошибки

Путают generics с any — говорят 'это как any, но с подсказками', не понимая, что тип фиксируется и проверяется

Не знают о constraints (extends) и думают, что внутри generic-функции можно обращаться к произвольным свойствам

Называют T 'типом дженерик' вместо 'параметром типа'

Не могут привести практический пример из реального кода (API-ответы, утилиты)

Путают generic-параметры и union-типы, предлагая использовать `T | U` там, где нужны два отдельных параметра

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

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

TypeScript с нуля

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

Feature-Sliced Design

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

Next.js - с нуля

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