Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Обмеження в дженериках». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Constraint** - це умова вигляду `T extends ...`, якою ви **обмежуєте множину допустимих підстановок** для параметра типу `T`. **Ключове:** мета - щоб у тілі функції чи типу можна було **безпечно звертатися** до властивостей/методів, які гарантовано є у `T`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке constraint Constraint - це умова вигляду `T extends ...`, якою ви **обмежуєте множину допустимих підстановок** для параметра типу `T`. Мета - щоб у тілі функції/типу ви могли **безпечно звертатися** до властивостей/методів, які гарантовано є у `T`. ```javascript function len<T extends { length: number }>(x: T) { return x.length; // безпечно: у T є length } ``` Якщо переданий тип не задовольняє обмеження - буде помилка на етапі компіляції. --- ## Основні форми обмежень ### 1) Структурні (shape/structural) Обмежуємо "форму" типу: потрібні поля та їхні типи. ```javascript function save<T extends { id: string; name: string }>(entity: T) { /* ... */ } ``` > Плюс: гнучко працює з "багатими" типами (надмножина полів допускається). --- ### 2) Обмеження за примітивами/об'єднаннями Дозволяємо тільки задані базові типи або їхнє об'єднання. ```javascript function toStr<T extends string | number | boolean>(x: T): string { return String(x); } ``` --- ### 3) "Тільки об'єкти" Часто вживані варіанти: ```javascript function f1<T extends object>(x: T) {} // виключає примітиви function f2<T extends Record<string, unknown>>(x: T) {}// ключі: string, значення: unknown function f3<T extends Record<PropertyKey, unknown>>(x: T) {} // допускає string|number|symbol ключі ``` > `object` - широкий, але **виключає** `null`, `undefined` і примітиви. > `Record` - точніше задає форму словника. --- ### 4) Залежні параметри: `keyof` + індексний доступ Класичний патерн: ключ залежить від об'єкта, значення - від ключа. ```javascript function getProp<T extends object, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } ``` --- ### 5) Масиви/кортежі/readonly-масиви ```javascript function first<T extends readonly unknown[]>(arr: T) { return arr[0]; // тип - T[number] / елемент кортежу } const a = first([1, 2, 3] as const); // 1 | 2 | 3 ``` > `readonly unknown[]` запобігає мутації; `T extends any[]` - мутабельний масив. --- ### 6) Callable/constructable-типи Обмежуємося функціями або класами. ```javascript type AnyFn = (...args: any[]) => any; function wrap<T extends AnyFn>(fn: T) { /* ... */ } type Ctor<A extends any[] = any[], R = any> = new (...args: A) => R; function make<T extends Ctor>(C: T): InstanceType<T> { return new C(); } ``` --- ### 7) Шаблонні рядкові типи Обмежуємося рядками певного "формату". ```javascript type ID = `id_${number}`; function load<T extends ID>(id: T) { /* ... */ } load("id_42"); // добре load("user_42"); // Помилка ``` --- ### 8) Виключення `null`/`undefined` ```javascript function ensure<T extends NonNullable<unknown>>(x: T) { // x не null і не undefined } ``` --- ## Як constraints впливають на виведення типів (inference) 1. **Уточнюють виведення**: компілятор шукає найвужче значення в межах заданої множини. ```javascript function id<T extends string | number>(x: T): T { return x; } const a = id(42); // T = 42 (літеральний), потім звужується до number за потреби ``` 2. **Дозволяють пов'язати параметри**: без `K extends keyof T` компілятор не гарантує, що ключ валідний для `T`. 3. **Покращують автодоповнення**: усередині тіла функції доступні методи/поля з обмеження. > Важливо: `extends` - це **constraint** у параметрах типу. Не плутати з `extends` в **умовних типах** (`T extends U ? X : Y`), там це **перевірка підтипу** для вибору гілки, а не обмеження параметра. --- ## Комбінування та прийоми ### Перетини для "мінімального набору полів" ```javascript type AtLeast<T, K extends keyof T> = Pick<T, K> & Partial<T>; function useUser<U extends AtLeast<{ id: string; name: string; age: number }, "id" | "name">>(u: U) {} ``` ### Обмеження + значення за замовчуванням ```javascript function pair<T, U extends string | number = string>(a: T, b?: U): [T, U] { return [a, b as U]; } ``` ### "Тільки ті ключі, що відповідають типу" ```javascript type KeysOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]; function pickByType<T extends object, V>(obj: T, key: KeysOfType<T, V>): V { return obj[key] as V; } ``` --- ## Часті помилки та пастки - **Занадто широкий** `object`: не дає гарантій щодо значень/ключів. Для словників краще `Record<PropertyKey, unknown>`. - **Забули** `keyof`**-зв'язку**: `function g<T, K>(obj: T, key: K)` - ключ не прив'язаний до `T`. - **Обмеження занадто вузьке**: ламає inference (TS перестає виводити літеральні типи). Розширюйте до об'єднань/шаблонів. - **Не можна звузити в тілі**: якщо `T extends { x: number }`, ви **можете використовувати** `x`, але **не можете** "звузити `T` до конкретного варіанта" без додаткових перевірок/умовних типів. --- ## Короткий підсумок - Використовуйте `T extends ...`, щоб **контролювати** допустимі підстановки і **отримати гарантії** всередині реалізації. - Основні патерни: **структурний shape**, **об'єднання примітивів**, **словники (**`Record`**)**, **залежні ключі (**`K extends keyof T`**)**, **масиви/кортежі/readonly**, **функції/конструктори**, **шаблонні рядки**. - Constraints **покращують inference** та автодоповнення, роблять узагальнений код реально **типобезпечним**.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.