Что такое generics в TypeScript?
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` там, где нужны два отдельных параметра


