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-параметрах
- Обмежує підставлюваний тип -
Tповинен розширювати (бути підтипом) вказаного типу. - Робить властивості гарантованими - можна звертатися до них без помилок.
- Покращує виведення типів (type inference) - TS знає більше про можливу структуру
T. - Не створює наслідування! - це не
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
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.