Что такое дженерики
Дженерики (обобщения) позволяют писать код, работающий с разными типами, сохраняя типовую безопасность. Вместо конкретного типа используется параметр типа.
Проблема без дженериков
Функция, которая возвращает первый элемент массива:
function first(items: any[]): any {
return items[0];
}
const n = first([1, 2, 3]); // any
n.toFixed(2); // нет ошибки компиляции, но тип потерян
any ломает типизацию: TypeScript не знает, что вернётся.
С дженериком
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([1, 2, 3]); // number | undefined
const s = first(['a', 'b']); // string | undefined
T — параметр типа. TypeScript выводит его из аргумента и сохраняет точный тип.
Синтаксис
function identity<T>(value: T): T {
return value;
}
<T>— объявление параметраTиспользуется как тип аргумента и возврата
Имя T — не обязательное. Можно TValue, TData, Item:
function identity<Value>(value: Value): Value {
return value;
}
По соглашению для простых случаев используют T, U, V, K.
Явное указание
identity<string>('hello');
identity<number>(42);
Обычно не нужно — TypeScript выводит.
Дженерик-типы
type Box<T> = {
value: T;
};
const stringBox: Box<string> = { value: 'hello' };
const numberBox: Box<number> = { value: 42 };
Дженерик-интерфейсы
interface Pair<K, V> {
key: K;
value: V;
}
const entry: Pair<string, number> = { key: 'age', value: 25 };
Дженерик-классы
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const numbers = new Stack<number>();
numbers.push(1);
numbers.push('a'); // ошибка
Зачем нужны
- Один код для разных типов
- Типовая безопасность сохраняется
- Нет дублирования
- Работает с inference (вывод типа)
Итоги
- Дженерики — параметры типа
- Синтаксис
<T> - Работают с функциями, типами, интерфейсами, классами
- TypeScript выводит тип из аргумента
- Сохраняют типовую безопасность