Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое стандартный Generic?
Generic (обобщение, шаблон) в TypeScript — это способ создания компонентов (функций, классов, интерфейсов), которые работают с множеством типов данных, сохраняя при этом информацию о типе. Это основной механизм обеспечения типобезопасности при работе с переиспользуемым кодом.
Основная идея
Generic позволяет писать код, который работает с различными типами данных, но при этом сохраняет информацию о конкретном используемом типе.
// Без Generic — теряем информацию о типе
function identity(value: any): any {
return value;
}
const result = identity(42);
// результат имеет тип any, мы не знаем, что это число
// С Generic — сохраняем информацию о типе
function identity<T>(value: T): T {
return value;
}
const result = identity(42);
// result имеет тип number
const text = identity('hello');
// text имеет тип string
Синтаксис Generic
T — это переменная типа, которая будет заменена на конкретный тип при использовании:
// T — типовая переменная
function getValue<T>(value: T): T {
return value;
}
// Явное указание типа
const num = getValue<number>(42);
// Неявный вывод типа (type inference)
const str = getValue('hello'); // T выводится как string
Generic в функциях
// Простая функция с одним типовым параметром
function getFirst<T>(array: T[]): T {
return array[0];
}
const firstNumber = getFirst([1, 2, 3]); // T = number
const firstString = getFirst(['a', 'b']); // T = string
// Несколько типовых параметров
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const p = pair('hello', 42); // T = string, U = number
const result: [string, number] = p;
// Функция с ограничением типа
function getLength<T extends { length: number }>(value: T): number {
return value.length;
}
getLength('hello'); // ✅ строки имеют length
getLength([1, 2, 3]); // ✅ массивы имеют length
getLength(42); // ❌ ошибка: number не имеет length
Generic в интерфейсах и типах
// Generic интерфейс
interface Container<T> {
value: T;
getValue(): T;
setValue(value: T): void;
}
// Реализация Generic интерфейса
class Box<T> implements Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
// Использование
const numberBox = new Box<number>(42);
const stringBox = new Box<string>('hello');
// Generic тип
type Response<T> = {
data: T;
status: number;
message: string;
};
const userResponse: Response<{ id: number; name: string }> = {
data: { id: 1, name: 'John' },
status: 200,
message: 'Success',
};
Generic в классах
// Generic класс
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
// Использование
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
const top = numberStack.pop(); // тип: number | undefined
const stringStack = new Stack<string>();
stringStack.push('hello');
const word = stringStack.pop(); // тип: string | undefined
Ограничения типов (Constraints)
// Ограничение: T должен быть объектом с определёнными свойствами
function printName<T extends { name: string }>(obj: T): void {
console.log(obj.name);
}
printName({ name: 'John', age: 30 }); // ✅
printName({ age: 30 }); // ❌ ошибка: нет свойства name
// Ограничение: T должен быть расширением определённого типа
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = merge({ a: 1 }, { b: 2 });
// merged имеет тип { a: number } & { b: number }
// Ограничение: T должен быть ключом другого типа
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const obj = { name: 'John', age: 30 };
const name = getProperty(obj, 'name'); // ✅ 'name' существует
getProperty(obj, 'email'); // ❌ ошибка: 'email' не в объекте
Практические примеры
Пример 1: API запрос с Generic
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
return response.json();
}
// Использование
interface User {
id: number;
name: string;
email: string;
}
const response = await fetchData<User>('/api/user/1');
// response.data имеет тип User
console.log(response.data.name);
Пример 2: Generic утилита для фильтрации
function filterByProperty<T, K extends keyof T>(
items: T[],
property: K,
value: T[K]
): T[] {
return items.filter(item => item[property] === value);
}
const users = [
{ id: 1, name: 'John', active: true },
{ id: 2, name: 'Jane', active: false },
{ id: 3, name: 'Bob', active: true },
];
const activeUsers = filterByProperty(users, 'active', true);
// activeUsers содержит только пользователей с active: true
Пример 3: Generic хук React
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then((data: T) => setData(data))
.catch(err => setError(err))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
// Использование
interface Post {
id: number;
title: string;
body: string;
}
const { data: posts, loading } = useFetch<Post[]>('/api/posts');
// posts имеет тип Post[] | null
Default типы для Generic
interface Container<T = string> {
value: T;
}
const container1: Container = { value: 'hello' }; // T = string по умолчанию
const container2: Container<number> = { value: 42 }; // T явно указан
Условные типы (Conditional Types)
// Условный тип в зависимости от значения T
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<number>; // false
// Практический пример
type Flatten<T> = T extends Array<infer U> ? U : T;
type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number
Лучшие практики
// ✅ ПРАВИЛЬНО
function identity<T>(value: T): T {
return value;
}
// ✅ Используй ограничения типов
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
// ❌ НЕПРАВИЛЬНО — слишком общий Generic
function process<T>(data: T): any {
return data; // Теряется типизация
}
// ✅ ПРАВИЛЬНО — специфичный Generic
function process<T extends object>(data: T): T {
return data; // Сохраняется типизация
}
Вывод: Generic — это фундаментальный инструмент TypeScript, обеспечивающий типобезопасность и переиспользуемость кода. Правильное использование Generic делает код более надёжным и удобнее в использовании.