Ответ
Да, активно использую дженерики (Generics) в TypeScript для Node.js-проектов. Они необходимы для создания переиспользуемых, типобезопасных компонентов, когда точный тип известен только в момент использования.
Практические примеры из моего опыта:
-
Универсальные функции-хелперы:
// Функция для оборачивания ответа API в стандартный формат interface ApiResponse<T> { success: boolean; data: T; error?: string; } function createResponse<T>(data: T, error?: string): ApiResponse<T> { return { success: !error, data, ...(error && { error }) }; } // Использование с разными типами данных const userResponse = createResponse<User>({ id: 1, name: 'John' }); const listResponse = createResponse<string[]>(['item1', 'item2']); -
Типизация репозиториев для работы с БД (паттерн Repository):
interface IRepository<T> { findById(id: number): Promise<T | null>; create(entity: Partial<T>): Promise<T>; update(id: number, updates: Partial<T>): Promise<T | null>; } class UserRepository implements IRepository<User> { async findById(id: number): Promise<User | null> { // ... логика поиска пользователя в БД } // ... реализация других методов } -
Работа с промисами и асинхронными операциями:
async function fetchData<T>(url: string): Promise<T> { const response = await fetch(url); if (!response.ok) throw new Error('Ошибка сети'); return response.json() as Promise<T>; // Указываем ожидаемый тип данных } // Вызов с конкретным типом interface Product { id: number; title: string; } const product = await fetchData<Product>('/api/products/1');
Дженерики делают код более предсказуемым и защищают от ошибок типов на этапе компиляции.
Ответ 18+ 🔞
Ну, слушай, я тут подумал — дженерики в TypeScript, это ж просто ёперный театр! Как будто у тебя есть один универсальный ключ, который подходит ко всем замкам, только ты заранее знаешь, к какому именно замку он сейчас полезет. Без них — пиздопроебина полная, начинаешь везде any пихать, а потом волнение ебать, когда в рантайме всё разваливается.
Вот смотри, реальные примеры, где без них нихуя:
-
Хелперы, которые не хотят быть идиотами. Ну представь, тебе надо обернуть ответ от API в одну обёртку. Без дженериков ты либо плодишь кучу почти одинаковых интерфейсов, либо ставишь
data: anyи молишься. А с ними — красота.// Объявляем формат ответа: успех, данные (любого типа Т) и опциональная ошибка interface ApiResponse<T> { success: boolean; data: T; error?: string; } // Функция, которая это создаёт. Тип Т она узнает, когда её вызовут. function createResponse<T>(data: T, error?: string): ApiResponse<T> { return { success: !error, data, ...(error && { error }) }; } // И вот магия: вызываем с разным хламом, и всё типобезопасно! const userResponse = createResponse<User>({ id: 1, name: 'Вася' }); // ApiResponse<User> const listResponse = createResponse<string[]>(['хлеб', 'молоко']); // ApiResponse<string[]>Раньше
dataбыл быany, и можно было бы туда запихнуть что угодно, а теперь — ни хуя себе, контроль. -
Репозитории для базы данных. Тут вообще без вариантов. Паттерн Repository — это когда у тебя для каждой сущности (User, Product) методы одни и те же (
find,create,update), но работают с разными типами. Дженерики спасают от писанины.// Общий контракт для всех репозиториев. T — это та сущность, с которой он работает. interface IRepository<T> { findById(id: number): Promise<T | null>; create(entity: Partial<T>): Promise<T>; update(id: number, updates: Partial<T>): Promise<T | null>; } // Реализуем для пользователя. Говорим: "Берём общий шаблон IRepository и вместо T везде подставляем User". class UserRepository implements IRepository<User> { async findById(id: number): Promise<User | null> { // ... идём в базу, ищем юзера } // ... остальные методы }Теперь если в методе
updateдляUserRepositoryя попробую передать полеprice, TypeScript сразу скажет: "Чувак, ты охуел? УUserнет поляprice". И это офигенно. -
Асинхронщина и промисы. Частая задача: написать функцию-обёртку для
fetch. Без дженериков она возвращала быPromise<any>, и опять ты в темноте.async function fetchData<T>(url: string): Promise<T> { const response = await fetch(url); if (!response.ok) throw new Error('Ну всё, опять ошибка сети, ебать колотить'); return response.json() as Promise<T>; // Говорим компилятору: "Поверь мне, там будет тип T" } // Используем. Говорим функции, что ждём с сервера именно продукт. interface Product { id: number; title: string; } const product = await fetchData<Product>('/api/products/1'); // product теперь имеет тип ProductЕсли сервер вдруг пришлёт тебе массив, а ты ждёшь один объект — TypeScript тебя предупредит, когда ты начнёшь обращаться к
product.title, а тамundefined. Удивление пиздец, но приятное, потому что на этапе компиляции, а не в продакшене.
Короче, дженерики — это не какая-то академическая хуйня. Это реальный инструмент, который делает код крепче, как хороший самогон. Терпения ноль ебать на отладку глупых ошибок с типами, когда можно заставить компилятор делать эту работу за тебя.