Пошук уроків, статей та іншого контенту
Розширюємо інтерфейси, повторно використовуючи властивості базових контрактів.
Інтерфейс у TypeScript описує форму об’єкта: які властивості та методи він повинен мати.
Якщо один інтерфейс є спеціалізованою версією іншого, його можна розширити за допомогою ключового слова extends. Новий інтерфейс успадкує всі властивості базового інтерфейсу та зможе додати власні.
interface User {
id: number;
name: string;
}
interface Admin extends User {
permissions: string[];
}
const admin: Admin = {
id: 1,
name: "Олена",
permissions: ["manage_users", "edit_settings"],
};Інтерфейс Admin містить:
id і name з інтерфейсу User;
permissions, додану безпосередньо в Admin.
Тому об’єкт типу Admin повинен мати всі три властивості.
extendsРозширення інтерфейсів допомагає:
не дублювати однакові властивості;
будувати типи поступово;
показувати зв’язок між базовою та спеціалізованою сутностями;
легше підтримувати код.
Без extends довелося б повторити властивості вручну:
interface User {
id: number;
name: string;
}
interface Admin {
id: number;
name: string;
permissions: string[];
}Такий код працює, але дублювання створює ризик помилок. Якщо властивість name потрібно буде змінити, її доведеться оновлювати в кількох місцях.
З extends спільні властивості описуються лише один раз:
interface User {
id: number;
name: string;
}
interface Admin extends User {
permissions: string[];
}Похідний інтерфейс може додавати будь-які нові властивості.
interface Product {
id: number;
title: string;
price: number;
}
interface DigitalProduct extends Product {
downloadUrl: string;
fileSize: number;
}
const book: DigitalProduct = {
id: 10,
title: "TypeScript для початківців",
price: 299,
downloadUrl: "https://example.com/book",
fileSize: 2048,
};DigitalProduct успадковує три властивості від Product і додає ще дві:
downloadUrl;
fileSize.
Якщо пропустити будь-яку обов’язкову властивість, TypeScript повідомить про помилку.
const invalidBook: DigitalProduct = {
id: 10,
title: "TypeScript для початківців",
price: 299,
// Помилка: відсутні downloadUrl і fileSize
};Один інтерфейс може розширювати кілька інших інтерфейсів. У такому разі він успадковує властивості від усіх батьківських інтерфейсів.
interface HasId {
id: number;
}
interface HasTimestamps {
createdAt: Date;
updatedAt: Date;
}
interface Article extends HasId, HasTimestamps {
title: string;
content: string;
}
const article: Article = {
id: 1,
createdAt: new Date("2025-01-01"),
updatedAt: new Date("2025-01-02"),
title: "Розширення інтерфейсів",
content: "Навчальний матеріал",
};Article має властивості:
id з HasId;
createdAt і updatedAt з HasTimestamps;
title і content, оголошені в самому Article.
Усі батьківські інтерфейси потрібно вказати після extends через кому:
interface Article extends HasId, HasTimestamps {
title: string;
}Якщо базовий інтерфейс має необов’язкову властивість, у похідному інтерфейсі вона також залишається необов’язковою.
interface User {
id: number;
name: string;
email?: string;
}
interface Moderator extends User {
section: string;
}
const moderator: Moderator = {
id: 2,
name: "Андрій",
section: "Коментарі",
};Властивість email не вказана, але об’єкт залишається правильним, оскільки в User вона оголошена через ?.
Похідний інтерфейс може зробити таку властивість обов’язковою:
interface RegisteredUser extends User {
email: string;
}
const registeredUser: RegisteredUser = {
id: 3,
name: "Марія",
email: "maria@example.com",
};Тепер для RegisteredUser властивість email потрібно вказувати.
Похідний інтерфейс може повторно оголосити властивість базового інтерфейсу, але її тип має залишатися сумісним із базовим типом.
Наприклад, властивість із літеральним типом може уточнити значення:
interface User {
role: string;
}
interface Admin extends User {
role: "admin";
}
const admin: Admin = {
role: "admin",
};У User властивість role може містити будь-який рядок. У Admin вона уточнена: значенням може бути лише "admin".
Несумісний тип призведе до помилки:
interface BaseConfig {
timeout: number;
}
interface InvalidConfig extends BaseConfig {
// Помилка: string несумісний із number
timeout: string;
}Якщо властивість базового інтерфейсу має тип number, похідний інтерфейс не може змінити його на string.
Інтерфейс може містити методи, а похідний інтерфейс — додавати нові методи.
interface Shape {
color: string;
getArea(): number;
}
interface Rectangle extends Shape {
width: number;
height: number;
}
const rectangle: Rectangle = {
color: "blue",
width: 10,
height: 5,
getArea() {
return this.width * this.height;
},
};
console.log(rectangle.getArea());Rectangle повинен реалізувати:
властивість color;
метод getArea;
властивості width і height.
Інтерфейс описує лише вимоги до об’єкта. Він не містить реалізації методу.
У прикладі нижче створимо базовий інтерфейс для користувача, а потім розширимо його для автора та адміністратора.
interface User {
id: number;
name: string;
}
interface Author extends User {
articlesCount: number;
publishArticle(): void;
}
interface Administrator extends User {
permissions: string[];
}
const author: Author = {
id: 1,
name: "Олена",
articlesCount: 5,
publishArticle() {
this.articlesCount += 1;
console.log(`${this.name} опублікувала статтю`);
},
};
const administrator: Administrator = {
id: 2,
name: "Андрій",
permissions: ["manage_users", "manage_articles"],
};
author.publishArticle();
console.log(author.articlesCount);
console.log(administrator.permissions);Обидва інтерфейси успадковують id і name від User, але мають різні додаткові властивості:
Author має кількість статей і метод публікації;
Administrator має список дозволів.
extendsВикористовуйте extends, коли:
новий інтерфейс є різновидом уже наявної сутності;
кілька інтерфейсів мають спільні властивості;
потрібно додати до базового контракту нові обов’язкові властивості;
потрібно логічно пов’язати базовий і спеціалізований типи.
Наприклад:
interface Vehicle {
brand: string;
speed: number;
}
interface Car extends Vehicle {
doors: number;
}
interface Bicycle extends Vehicle {
hasBasket: boolean;
}Car і Bicycle є різними видами транспортних засобів, тому спільні властивості зручно винести в Vehicle.
Похідний інтерфейс успадковує всі обов’язкові властивості базового.
interface Message {
id: number;
text: string;
}
interface ImageMessage extends Message {
imageUrl: string;
}
const message: ImageMessage = {
id: 1,
imageUrl: "image.png",
// Помилка: відсутня властивість text
};Потрібно додати text:
const validMessage: ImageMessage = {
id: 1,
text: "Зображення",
imageUrl: "image.png",
};Не можна змінити тип успадкованої властивості на несумісний.
interface Product {
price: number;
}
interface WrongProduct extends Product {
// Помилка: price має залишатися сумісною з number
price: string;
}extends не замінює базовий інтерфейс. Похідний інтерфейс містить його властивості та доповнює контракт.
interface BasicUser {
id: number;
}
interface DetailedUser extends BasicUser {
email: string;
}
// DetailedUser має і id, і emailextends розширює один інтерфейс іншим.
Похідний інтерфейс успадковує всі властивості та методи базового.
Новий інтерфейс може додавати власні властивості й методи.
Один інтерфейс може розширювати кілька інтерфейсів.
Успадковану властивість можна уточнити лише сумісним типом.
Розширення інтерфейсів допомагає повторно використовувати спільні контракти та уникати дублювання.