Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як обмежити generic-тип ключами іншого типу?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Найпростіший спосіб - **`K extends keyof T`**: `keyof T` дає об'єднання ключів типу `T`, і параметр `K` обмежується цим об'єднанням, наприклад `function getProp<T, K extends keyof T>(obj: T, key: K): T[K]`. **Ключове:** цей базовий патерн можна звужувати - перетином з конкретним набором ключів, фільтрацією за типом значення, або через `Extract<keyof T, number>` для масивів.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 1) Базовий патерн: ключ об'єкта ```javascript function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { id: 1, name: "Tim" }; getProp(user, "id"); // OK getProp(user, "age"); // Помилка: "age" ∉ keyof typeof user ``` - `keyof T` - об'єднання рядкових/числових/символьних ключів типу `T`. --- ## 2) Дозволити лише підмножину ключів Іноді потрібні не **всі** ключі `T`, а лише частина: ```javascript function getIdOrName< T, K extends keyof T & ("id" | "name") >(obj: T, key: K): T[K] { return obj[key]; } ``` - Тут `K` - перетин «ключі T» з конкретною множиною `"id" | "name"`. --- ## 3) Ключі, чиї **значення** відповідають типу (фільтрація за значенням) ```javascript type KeysOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]; function getNumberField< T, K extends KeysOfType<T, number> >(obj: T, key: K): number { return obj[key] as number; } type U = { a: number; b: string; c: number }; /* K = "a" | "c" */ ``` Схожий прийом часто називають *value-based key filtering*. --- ## 4) Обов'язкові/необов'язкові/незаписувані ключі Іноді потрібно обмежити ключі за «кваліфікатором» (optional/readonly): ```javascript type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T]; type WritableKeys<T> = { [K in keyof T]-?: { [P in K]: T[P] } extends { -readonly [P in K]: T[P] } ? K : never }[keyof T]; function takeRequired<T, K extends RequiredKeys<T>>(obj: T, key: K) { return obj[key]; } ``` --- ## 5) Обмеження ключами **індексної сигнатури** Якщо тип має словникову форму: ```javascript type Dict = Record<string, number>; // індексна сигнатура function getDict< T extends Record<string, unknown>, K extends keyof T >(obj: T, key: K) { return obj[key]; } ``` > `keyof T` при індексній сигнатурі ≈ `string | number | symbol` (залежить від ключів). --- ## 6) Ключі масивів і кортежів ```javascript function first<T extends readonly unknown[]>(arr: T) { const i: Extract<keyof T, number> = 0; // лише числові ключі return arr[i]; // T[number] } ``` У масиву `keyof T` включає й методи (`"length"`, `"push"`). Щоб обмежитися індексами, перетинайте з `number`. --- ## 7) Ключі в літеральних об'єктах: `as const` Щоб отримати **літеральні** ключі, а не «розширені»: ```javascript const cfg = { api: "/api", cdn: "/cdn", } as const; type Cfg = typeof cfg; // { readonly api: "/api"; readonly cdn: "/cdn" } type CfgKey = keyof Cfg; // "api" | "cdn" function fromCfg<K extends keyof Cfg>(k: K): Cfg[K] { return cfg[k]; } ``` --- ## 8) Ключі в `enum` / об'єктах-довідниках ```javascript enum Role { Admin = "admin", User = "user" } const ROLES = { Admin: "admin", User: "user" } as const; type RoleKey = keyof typeof ROLES; // "Admin" | "User" type RoleVal = typeof ROLES[RoleKey]; // "admin" | "user" function allow<K extends keyof typeof ROLES>(k: K) { /* ... */ } ``` --- ## 9) Утиліти для збирання API: `Pick`/`Record`/`Omit` з обмеженими ключами ```javascript function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { const out = {} as Pick<T, K>; keys.forEach(k => (out[k] = obj[k])); return out; } function mapValues<T, K extends keyof T>( obj: T, keys: K[], fn: (v: T[K], k: K) => T[K] ): Pick<T, K> { const res = {} as Pick<T, K>; for (const k of keys) res[k] = fn(obj[k], k); return res; } ``` --- ## 10) Часті граблі та підказки - `keyof never` -> `never`: якщо `T` може бути `never`, `K` вже невивідний; додавайте захист. - У масивів не забувайте фільтрувати ключі до `number`, інакше з'являться рядкові методи. - Якщо ключі приходять зі значення (масиву рядків), надавайте перевагу `as const`: ```javascript const allowed = ["id", "name"] as const; type Allowed = typeof allowed[number]; // "id" | "name" function f<T, K extends keyof T & Allowed>(obj: T, key: K) {} ``` --- ### Резюме - База: `K extends keyof T` - ключі `T`. - Підмножина: `K extends keyof T & ("a" | "b")`. - За типом значень: `KeysOfType<T, V>`. - Для масивів: `Extract<keyof T, number>`. - Для літералів/enums: `keyof typeof X` (+ `as const`).Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.