← Назад к вопросам

Для чего используется Generic?

2.0 Middle🔥 231 комментариев
#TypeScript#ООП

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего используется Generic (обобщённый тип)?

Определение

Generic — это механизм в TypeScript, который позволяет создавать переиспользуемые компоненты, функции и классы, которые работают с различными типами данных, сохраняя при этом типобезопасность.

Вместо написания одной функции для каждого типа данных, мы пишем одну функцию, которая может работать с любым типом.

Основные причины использования

1. Переиспользуемость кода

Без generics приходилось бы писать отдельные функции для каждого типа:

function getFirstString(arr: string[]): string { return arr[0]; }
function getFirstNumber(arr: number[]): number { return arr[0]; }
function getFirstBoolean(arr: boolean[]): boolean { return arr[0]; }

С generics — одна функция для всех типов:

function getFirst<T>(arr: T[]): T {
  return arr[0];
}

getFirst([1, 2, 3]);           // T = number
getFirst(['a', 'b', 'c']);     // T = string
getFirst([true, false]);        // T = boolean

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

const numbers = getFirst([1, 2, 3]);
// TypeScript знает, что numbers имеет тип number
// numbers.toUpperCase() — ОШИБКА! У числа нет такого метода

const strings = getFirst(['a', 'b', 'c']);
// TypeScript знает, что strings имеет тип string
strings.toUpperCase(); // OK!

Примеры использования

Функции с generics

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

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

const result = swap([1, 'hello']);
// result имеет тип [string, number]

Интерфейсы с generics

interface Repository<T> {
  getAll(): T[];
  getById(id: string): T | null;
  save(item: T): void;
}

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

class UserRepository implements Repository<User> {
  getAll(): User[] { /* ... */ }
  getById(id: string): User | null { /* ... */ }
  save(item: User): void { /* ... */ }
}

Классы с generics

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

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

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

const numberStack = new Stack<number>();
numberStack.push(1);
const val = numberStack.pop(); // тип number

Constraints (ограничения generics)

Иногда нужно ограничить, какие типы можно использовать:

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

const user = { name: 'John', age: 30 };
getProperty(user, 'name');  // OK
getProperty(user, 'email'); // ОШИБКА! email нет в объекте

Ограничение наследованием

interface HasId {
  id: string;
}

function printId<T extends HasId>(item: T): void {
  console.log(item.id);
}

printId({ id: '1', name: 'John' });  // OK
printId({ name: 'John' });           // ОШИБКА! Нет id

Практические преимущества

  • DRY принцип — не повторяем код для каждого типа
  • Автодополнение IDE — правильные методы и свойства
  • Ошибки на этапе разработки — не в production
  • Документирование — явно видно, с какими типами работает код
  • Рефакторинг — безопасно менять типы, IDE подскажет где нужны изменения

Реальный пример из backend

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

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

const userResponse = await fetchUser('123');
// userResponse.data имеет тип User автоматически!
userResponse.data.name; // OK

Generic — это мощный инструмент, который делает TypeScript код более гибким и безопасным одновременно.

Для чего используется Generic? | PrepBro