Skip to main content

extends у generic-параметрах

1. Загальна форма

javascript
function example<T extends Constraint>(value: T) { ... }
  • T - параметр типу (generic).
  • extends Constraint - обмеження, якому T повинен відповідати.

TypeScript перевіряє: чи можна T вважати підтипом Constraint. Якщо ні - компілятор видасть помилку.


Приклад 1: обмеження за "формою" (shape constraint)

javascript
function printLength<T extends { length: number }>(value: T) { console.log(value.length); } printLength("hello"); // string має length printLength([1, 2, 3]); // масив має length printLength(123); // число не має length

Тут T повинен бути типом, у якого є властивість length: number.


Приклад 2: обмеження за базовим типом

javascript
function toStringValue<T extends string | number>(value: T): string { return value.toString(); } toStringValue("abc"); // string toStringValue(123); // number toStringValue(true); // boolean не входить у допустиме об'єднання

Тут T обмежений об'єднанням: string | number.


Приклад 3: обмеження за інтерфейсом

javascript
interface Identifiable { id: string; } function logId<T extends Identifiable>(obj: T) { console.log(obj.id); } logId({ id: "123", name: "Tim" }); // OK logId({ name: "Alex" }); // немає id

T зобов'язаний містити поле id.


Приклад 4: залежні generic-параметри

javascript
function getProp<T, K extends keyof T>(obj: T, key: K) { return obj[key]; } const user = { id: 1, name: "Tim" }; getProp(user, "id"); // OK getProp(user, "age"); // "age" не є ключем user

Тут K залежить від T і обмежується лише допустимими ключами об'єкта.


Приклад 5: обмеження для класів і конструкторів

javascript
type Constructor<T> = new (...args: any[]) => T; function createInstance<T extends Constructor<any>>(Ctor: T) { return new Ctor(); }

T повинен бути конструктором (класом), щоб його можна було створити через new.


Приклад 6: обмеження для словників (Record)

javascript
function sumValues<T extends Record<string, number>>(obj: T): number { return Object.values(obj).reduce((a, b) => a + b, 0); } sumValues({ a: 1, b: 2 }); // OK sumValues({ x: 1, y: "two" }); // рядок не number

Record<string, number> означає: об'єкт, де ключі - рядки, а значення - числа.


Приклад 7: extends + значення за замовчуванням

javascript
function identity<T extends string | number = string>(value: T): T { return value; } identity("hi"); // OK identity(42); // OK identity(true); // boolean не підходить під constraint

Що насправді робить extends у generic-параметрах

  1. Обмежує підставлюваний тип - T повинен розширювати (бути підтипом) вказаного типу.
  2. Робить властивості гарантованими - можна звертатися до них без помилок.
  3. Покращує виведення типів (type inference) - TS знає більше про можливу структуру T.
  4. Не створює наслідування! - це не extends з класів, а логічне обмеження типу.

Приклад без extends -> помилка

javascript
function getLength<T>(value: T): number { return value.length; // TS Error: у T може не бути length }

Виправлено:

javascript
function getLength<T extends { length: number }>(value: T) { return value.length; // OK }

Підсумок

Призначення extendsПрикладЩо дає
Обмеження за формою<T extends { id: string }>Гарантує наявність полів
Обмеження за об'єднанням<T extends string | number>Тільки перелічені типи
Обмеження за інтерфейсом<T extends Identifiable>Тільки типи, що реалізують інтерфейс
Залежність параметрів<K extends keyof T>Ключ повинен належати об'єкту
Обмеження класу<T extends new (...args) => any>Тільки конструктори
Обмеження словника<T extends Record<string, number>>Об'єкти з визначеним типом значень

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.