Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего используется 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 код более гибким и безопасным одновременно.