webroad.online
  1. 1Веб
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Инструменты
  8. 8React
  9. 9Стейт-менеджмент
  10. 10Next.js
  11. 11Формы
  12. 12Данные и бэкенд
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Анимации
  16. 16Тестирование
  17. 17Архитектура
TypeScript · Урок 4 из 6

Дженерики

Типы с параметрами, ограничения через extends, keyof и T[K].

Обновлено

Что такое дженерики

Дженерики (generics) — это типы с параметрами. Как функция принимает значения в параметрах, так дженерик принимает типы.

Какую проблему решают:

function first(arr: any[]): any { return arr[0] }
const n = first([1, 2])       // any — потеряна информация, что это number

function first<T>(arr: T[]): T | undefined { return arr[0] }
const m = first([1, 2])       // number ✓
const s = first(['a'])        // string ✓

T — параметр типа, пустое место, которое TS автоматически заполняет по аргументам. Функция остаётся одной, но тип результата следует за типом входа.

Вы их уже используете

const ids: Array<number> = []                 // = number[]
const users = new Map<string, User>()
const [count, setCount] = useState<number>(0)
const res: Promise<User> = getUser()

Ограничения через extends

Иногда T не может быть чем угодно — у него должно быть что-то определённое:

function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b
}

longest('abc', 'de')        // ✓ у строки есть length
longest([1, 2], [3])        // ✓ у массива есть length
longest(10, 20)             // ✗ у number нет length

keyof и доступ по ключу

function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map(item => item[key])
}

pluck(users, 'email')       // string[]
pluck(users, 'emial')       // ✗ опечатка поймана

keyof T = union ключей T; T[K] = тип значения по ключу K.

Обобщённые типы

Обобщёнными бывают не только функции:

type ApiResponse<T> =
  | { ok: true; data: T }
  | { ok: false; error: string }

type Paginated<T> = { items: T[]; page: number; total: number }

const res: ApiResponse<Paginated<Product>> = await api('/products')

Когда нужны дженерики

Ситуация Дженерик?
функция, которая одинаково работает с любым типом (first, groupBy, pluck) да
тип-«контейнер» (ответ API, пагинация, состояние формы) да
переиспользуемый React-компонент для любого типа элементов (List<T>, Select<T>) да
функция, которая работает только с User нет — пишите User

Признак перебора: T встречается в сигнатуре только один раз. Тогда дженерик не нужен.

Коротко

  • Дженерик = тип с параметром; сохраняет связь между типом входа и типом выхода.
  • extends задаёт, что должно быть у T; keyof T + T[K] — для доступа по ключу.
  • Используйте для утилит и контейнеров, а не везде.

Официальные источники

Упражнения

Была ли страница полезной?

Один клик — без регистрации.