Пошук уроків, статей та іншого контенту
Навчіться описувати структуру об'єктів через анотації типів, type-аліаси та інтерфейси.
Об'єкт у JavaScript може містити властивості різних типів:
const user = {
name: "Олена",
age: 28,
isAdmin: false
};У TypeScript можна явно описати, які властивості повинен мати об'єкт і яких типів вони мають бути. Це допомагає:
знаходити помилки ще до запуску програми;
отримувати підказки в редакторі;
розуміти структуру даних;
безпечніше передавати об'єкти у функції.
Тип об'єкта можна записати безпосередньо після назви змінної:
const user: {
name: string;
age: number;
isAdmin: boolean;
} = {
name: "Олена",
age: 28,
isAdmin: false
};Після кожної властивості ставиться ; або ,. Найчастіше в описах типів використовують крапку з комою.
Описаний тип означає, що об'єкт повинен мати:
name типу string;
age типу number;
isAdmin типу boolean.
Якщо вказати неправильне значення, TypeScript повідомить про помилку:
const user: {
name: string;
age: number;
} = {
name: "Олена",
age: "28" // Помилка: очікується number
};Також помилкою буде відсутність обов'язкової властивості:
const user: {
name: string;
age: number;
} = {
name: "Олена"
// Помилка: відсутня властивість age
};Опис об'єкта особливо корисний для параметрів функцій. Так ми точно знаємо, які дані можна передати у функцію:
function printUser(user: {
name: string;
age: number;
}): void {
console.log(`${user.name}, ${user.age} років`);
}
printUser({
name: "Олена",
age: 28
});Під час виклику TypeScript перевіряє структуру переданого об'єкта:
printUser({
name: "Олена",
age: "28" // Помилка: age має бути number
});Такий запис зручний для невеликих одноразових типів. Але якщо одна й та сама структура використовується в кількох місцях, краще створити окремий тип.
type дозволяє дати назву типу. Така назва називається type-аліасом:
type User = {
name: string;
age: number;
isAdmin: boolean;
};Тепер цей тип можна використовувати для змінних, параметрів і значень, які повертають функції:
type User = {
name: string;
age: number;
isAdmin: boolean;
};
const firstUser: User = {
name: "Олена",
age: 28,
isAdmin: false
};
function isAdult(user: User): boolean {
return user.age >= 18;
}
console.log(isAdult(firstUser));Type-аліас описує форму об'єкта, але не створює об'єкт під час виконання програми.
Знак ? після назви властивості робить її необов'язковою:
type Product = {
name: string;
price: number;
description?: string;
};
const productWithoutDescription: Product = {
name: "Клавіатура",
price: 1200
};
const productWithDescription: Product = {
name: "Миша",
price: 800,
description: "Бездротова миша"
};Властивість description може бути відсутньою. Тому перед використанням варто враховувати, що її значення може бути undefined:
function printProduct(product: Product): void {
console.log(product.name);
console.log(product.description ?? "Опис відсутній");
}Оператор ?? використовує значення праворуч, якщо значення ліворуч дорівнює null або undefined.
Модифікатор readonly забороняє змінювати властивість після створення об'єкта:
type Account = {
readonly id: number;
owner: string;
};
const account: Account = {
id: 1,
owner: "Олена"
};
account.owner = "Андрій";
account.id = 2; // Помилка: id доступна лише для читанняreadonly захищає властивість на рівні перевірки TypeScript. Це не додає спеціального захисту під час виконання JavaScript.
Інтерфейс — ще один спосіб описати структуру об'єкта:
interface User {
name: string;
age: number;
isAdmin: boolean;
}Використання інтерфейсу виглядає так само, як використання type-аліасу:
interface User {
name: string;
age: number;
isAdmin: boolean;
}
const user: User = {
name: "Олена",
age: 28,
isAdmin: false
};
function printUser(user: User): void {
console.log(`${user.name}, ${user.age} років`);
}
printUser(user);Для опису звичайних об'єктів type та interface часто можна використовувати взаємозамінно:
type Product = {
name: string;
price: number;
};
interface Category {
title: string;
productsCount: number;
}Вибір залежить від стилю проєкту. Головне — використовувати один підхід послідовно.
Інтерфейс може розширювати інший інтерфейс за допомогою extends. Новий інтерфейс отримує всі властивості батьківського:
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
position: string;
}
const employee: Employee = {
name: "Андрій",
age: 32,
position: "Розробник"
};Employee повинен містити name, age і position.
Схожий результат можна отримати за допомогою перетину типів &:
type Person = {
name: string;
age: number;
};
type Employee = Person & {
position: string;
};Для початкового опису об'єктів достатньо пам'ятати:
type створює назву для типу;
interface описує структуру об'єкта;
обидва підходи дозволяють типізувати змінні та параметри функцій.
TypeScript перевіряє не назву типу, а структуру об'єкта. Це називається структурною типізацією.
type Point = {
x: number;
y: number;
};
const coordinates = {
x: 10,
y: 20,
color: "red"
};
const point: Point = coordinates;
console.log(point.x, point.y);Об'єкт coordinates має всі необхідні властивості Point, тому його можна присвоїти змінній типу Point. Додаткова властивість color не заважає в цьому випадку.
Водночас під час прямого передавання об'єкта TypeScript суворіше перевіряє зайві властивості:
function printPoint(point: Point): void {
console.log(point.x, point.y);
}
printPoint({
x: 10,
y: 20,
color: "red" // Помилка: зайва властивість у цьому об'єкті
});Якщо додаткові дані вже збережені у змінній, така передача може бути дозволена:
const coordinates = {
x: 10,
y: 20,
color: "red"
};
printPoint(coordinates);interface Product {
readonly id: number;
name: string;
price: number;
description?: string;
}
type Order = {
id: number;
customer: string;
products: Product[];
};
function calculateTotal(order: Order): number {
return order.products.reduce((total, product) => {
return total + product.price;
}, 0);
}
const keyboard: Product = {
id: 1,
name: "Клавіатура",
price: 1200,
description: "Механічна клавіатура"
};
const mouse: Product = {
id: 2,
name: "Миша",
price: 800
};
const order: Order = {
id: 101,
customer: "Олена",
products: [keyboard, mouse]
};
console.log(`Замовлення №${order.id}`);
console.log(`Клієнт: ${order.customer}`);
console.log(`Сума: ${calculateTotal(order)} грн`);У цьому прикладі:
Product описує структуру товару;
id не можна змінити після створення товару;
description є необов'язковою;
Order містить масив об'єктів типу Product;
функція calculateTotal приймає лише об'єкт, сумісний із типом Order.
Неправильно:
const user: object = {
name: "Олена",
age: 28
};Тип object лише повідомляє, що значення є об'єктом. Він не описує властивості, тому TypeScript не зможе безпечно працювати з user.name.
Краще:
type User = {
name: string;
age: number;
};
const user: User = {
name: "Олена",
age: 28
};anyТип any вимикає більшість перевірок TypeScript:
const user: any = {
name: "Олена",
age: 28
};
user.age.toUpperCase(); // Помилка виникне лише під час виконанняДля об'єктів краще описувати потрібні властивості через type або interface.
interface User {
name: string;
age: number;
}
const user: User = {
name: "Олена"
// Помилка: відсутня обов'язкова властивість age
};Якщо властивість справді може бути відсутньою, позначте її знаком ?:
interface User {
name: string;
age?: number;
}readonlytype User = {
readonly id: number;
name: string;
};
const user: User = {
id: 1,
name: "Олена"
};
user.id = 2; // Помилкаreadonly потрібно використовувати лише для значень, які не повинні змінюватися після створення об'єкта.
Структуру об'єкта можна описати безпосередньо в анотації типу.
type дозволяє створити повторно використовувану назву для типу.
interface також описує структуру об'єкта й підтримує розширення через extends.
Обов'язкові властивості потрібно вказувати без ?.
Знак ? робить властивість необов'язковою.
readonly забороняє змінювати властивість у коді TypeScript.
Типізація параметрів функцій допомагає передавати об'єкти правильної структури.
TypeScript порівнює структуру об'єктів, а не лише назви їхніх типів.