Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як обмежити параметр типу ключами іншого параметра: K extends keyof T?

Параметр типу може бути обмежений іншим параметром - так описують зв'язок між аргументами функції.

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

const users = [
  { id: 1, name: 'Оля', age: 28 },
  { id: 2, name: 'Іра', age: 31 },
];

pluck(users, 'name');    // string[]
pluck(users, 'age');     // number[]
pluck(users, 'email');   // помилка: 'email' немає серед ключів

Як це працює:

  1. TypeScript виводить T з першого аргументу - тип елементів масиву;
  2. K extends keyof T дозволяє другим аргументом лише ключі T;
  3. K виводиться як конкретний літерал ('name'), а не весь keyof T, тож тип повернення T[K][] - точний.

Інші приклади зв'язаних параметрів:

// значення має відповідати типу поля
function setField<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
  obj[key] = value;
}

setField(user, 'age', 30);       // ок
setField(user, 'age', '30');     // помилка: string не number

// групування
function groupBy<T, K extends keyof T>(items: T[], key: K): Map<T[K], T[]> {
  const groups = new Map<T[K], T[]>();
  for (const item of items) {
    const group = groups.get(item[key]) ?? [];
    group.push(item);
    groups.set(item[key], group);
  }
  return groups;
}

Типові помилки:

  • key: keyof T без окремого параметра: function getField<T>(obj: T, key: keyof T): T[keyof T] - тип повернення стає об'єднанням типів усіх полів. Окремий K зберігає зв'язок з конкретним ключем;
  • K extends string замість keyof T - тоді немає перевірки, що ключ існує;
  • символьні й числові ключі: keyof T може містити number | symbol. Якщо ключ використовується в рядкових операціях (шаблонні рядки), - K extends keyof T & string або Extract<keyof T, string>.

Цей прийом - основа багатьох бібліотек: типізовані таблиці (columnHelper.accessor('email')), форми (register('name') у React Hook Form), сховища стану з селекторами за ключем.

Докладніше в документації: Generics: параметри типу в обмеженнях

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання