Пошук уроків, статей та іншого контенту
Комбінуємо кілька типів через intersection types і створюємо об’єкти з об’єднаними властивостями.
Intersection type — це тип, який об’єднує кілька типів за допомогою оператора &.
Значення intersection type має відповідати одночасно всім типам, що входять до перетину.
type HasName = {
name: string;
};
type HasAge = {
age: number;
};
type Person = HasName & HasAge;
const person: Person = {
name: "Олена",
age: 28,
};Тип Person вимагає наявності і name, і age. Фактично він має такі властивості:
type Person = {
name: string;
age: number;
};Але запис через & зручний, коли потрібно складати типи з окремих логічних частин.
&Загальний синтаксис:
type Combined = TypeA & TypeB;Можна об’єднувати більше двох типів:
type HasId = {
id: number;
};
type HasTimestamps = {
createdAt: Date;
updatedAt: Date;
};
type HasStatus = {
status: "active" | "inactive";
};
type Entity = HasId & HasTimestamps & HasStatus;
const entity: Entity = {
id: 1,
createdAt: new Date(),
updatedAt: new Date(),
status: "active",
};Об’єкт типу Entity повинен містити всі властивості з HasId, HasTimestamps і HasStatus.
Intersection types часто використовують, щоб додати до базового типу додаткові властивості.
type Product = {
id: number;
name: string;
price: number;
};
type Timestamps = {
createdAt: Date;
updatedAt: Date;
};
type ProductWithTimestamps = Product & Timestamps;
const product: ProductWithTimestamps = {
id: 101,
name: "Клавіатура",
price: 2500,
createdAt: new Date("2026-01-10"),
updatedAt: new Date("2026-02-01"),
};Тут:
Product описує властивості товару;
Timestamps описує дати створення та оновлення;
ProductWithTimestamps містить властивості обох типів.
Це допомагає повторно використовувати типи й не дублювати однакові властивості.
Intersection types можна створювати не лише з type aliases, а й з інтерфейсами.
interface User {
username: string;
}
interface HasEmail {
email: string;
}
type RegisteredUser = User & HasEmail;
const registeredUser: RegisteredUser = {
username: "marta",
email: "marta@example.com",
};Типи, що входять до перетину, можуть бути оголошені різними способами:
interface User {
username: string;
}
type HasRole = {
role: "admin" | "editor";
};
type UserWithRole = User & HasRole;Intersection type корисний, коли функція працює з об’єктом, який повинен мати кілька наборів властивостей.
type Authenticated = {
userId: number;
token: string;
};
type HasPermissions = {
permissions: string[];
};
type AuthorizedRequest = Authenticated & HasPermissions;
function canEdit(request: AuthorizedRequest): boolean {
return request.permissions.includes("edit");
}
const request: AuthorizedRequest = {
userId: 42,
token: "abc123",
permissions: ["read", "edit"],
};
console.log(canEdit(request)); // trueФункція canEdit приймає тільки об’єкт, у якому одночасно є:
userId;
token;
permissions.
Якщо якоїсь обов’язкової властивості немає, TypeScript повідомить про помилку під час перевірки типів.
Об’єкт можна створити поступово, а потім передати у функцію з intersection type:
type ContactInfo = {
email: string;
phone: string;
};
type Address = {
city: string;
country: string;
};
type Customer = ContactInfo & Address;
const contactInfo: ContactInfo = {
email: "client@example.com",
phone: "+380501234567",
};
const address: Address = {
city: "Львів",
country: "Україна",
};
const customer: Customer = {
...contactInfo,
...address,
};
function describeCustomer(customer: Customer): string {
return `${customer.email}, ${customer.city}, ${customer.country}`;
}
console.log(describeCustomer(customer));Оператор spread об’єднує властивості двох об’єктів, а тип Customer описує очікуваний результат.
Якщо властивість є обов’язковою хоча б в одному з типів, для intersection type вона також буде обов’язковою.
type BasicUser = {
name: string;
};
type OptionalDetails = {
age?: number;
};
type UserWithDetails = BasicUser & OptionalDetails;
const user: UserWithDetails = {
name: "Андрій",
};У цьому прикладі:
name є обов’язковою;
age є необов’язковою.
Властивість age можна додати, але TypeScript не вимагає її наявності.
const anotherUser: UserWithDetails = {
name: "Ірина",
age: 31,
};Типи в intersection повинні бути сумісними. Якщо одна й та сама властивість має несумісні типи, результат може стати типом never.
type WithStringId = {
id: string;
};
type WithNumberId = {
id: number;
};
type Conflicting = WithStringId & WithNumberId;Щоб значення було типу Conflicting, id мало б одночасно бути і string, і number. Такого значення не існує, тому тип властивості id стає never.
const value: Conflicting = {
// Помилка: string не можна призначити типу never
id: "abc",
};Несумісні типи не слід об’єднувати через &. Потрібно перевірити модель даних і вирішити, який тип властивості має бути правильним.
Intersection type і union type вирішують різні задачі.
&Значення повинно відповідати всім типам:
type HasName = {
name: string;
};
type HasAge = {
age: number;
};
type Person = HasName & HasAge;
const person: Person = {
name: "Олег",
age: 35,
};|Значення може відповідати одному з типів:
type Admin = {
role: "admin";
};
type Guest = {
role: "guest";
};
type User = Admin | Guest;
const admin: User = {
role: "admin",
};Зручно запам’ятати:
A & B — потрібно мати можливості і A, і B;
A | B — можна бути або A, або B.
Intersection працює не лише з об’єктами. Наприклад, перетин несумісних примітивних типів є неможливим:
type Impossible = string & number;Жодне значення не може одночасно бути рядком і числом, тому Impossible фактично дорівнює never.
На практиці intersection types найчастіше застосовують саме для об’єднання типів об’єктів.
За допомогою перетинів можна описати об’єкт через незалежні характеристики:
type Identifiable = {
id: string;
};
type Named = {
name: string;
};
type Loggable = {
log(): void;
};
type NamedEntity = Identifiable & Named & Loggable;
const entity: NamedEntity = {
id: "item-1",
name: "Документ",
log() {
console.log(`Об'єкт: ${this.name}, id: ${this.id}`);
},
};
entity.log();Кожен окремий тип відповідає за одну характеристику:
Identifiable — ідентифікатор;
Named — назву;
Loggable — метод журналювання.
NamedEntity поєднує всі ці характеристики в один тип.
& і |type Contact = {
email: string;
};
type Phone = {
phone: string;
};
type Customer = Contact & Phone;Customer повинен мати і email, і phone. Якщо потрібна можливість мати лише один із варіантів, це вже інша модель і зазвичай використовується union type.
type HasId = {
id: number;
};
type HasName = {
name: string;
};
type Entity = HasId & HasName;
const entity: Entity = {
id: 1,
// Помилка: відсутня обов'язкова властивість name
};Intersection type не робить властивості необов’язковими. Він збирає вимоги всіх типів.
type StringValue = {
value: string;
};
type NumberValue = {
value: number;
};
type InvalidValue = StringValue & NumberValue;Однакові властивості в типах перетину повинні мати сумісні типи. Інакше отриманий тип буде непридатним для створення звичайного значення.
Intersection type існує лише на рівні TypeScript і не об’єднує об’єкти автоматично під час виконання.
type A = {
a: number;
};
type B = {
b: number;
};
type Combined = A & B;
const first: A = { a: 1 };
const second: B = { b: 2 };
const combined: Combined = {
...first,
...second,
};Для створення об’єкта з властивостями обох частин потрібно реально об’єднати ці властивості, наприклад за допомогою spread.
type Account = {
id: number;
username: string;
};
type Subscription = {
plan: "free" | "pro";
expiresAt: Date;
};
type NotificationSettings = {
emailNotifications: boolean;
};
type UserProfile = Account & Subscription & NotificationSettings;
function printProfile(profile: UserProfile): void {
const plan = profile.plan === "pro" ? "Pro" : "Free";
console.log(`Користувач: ${profile.username}`);
console.log(`Тариф: ${plan}`);
console.log(`Email-сповіщення: ${profile.emailNotifications ? "увімкнені" : "вимкнені"}`);
console.log(`Підписка дійсна до: ${profile.expiresAt.toLocaleDateString("uk-UA")}`);
}
const profile: UserProfile = {
id: 7,
username: "software_dev",
plan: "pro",
expiresAt: new Date("2026-12-31"),
emailNotifications: true,
};
printProfile(profile);Тип UserProfile складається з трьох незалежних частин:
даних облікового запису;
даних підписки;
налаштувань сповіщень.
Якщо додати нову властивість до одного з базових типів, TypeScript автоматично врахує її в UserProfile.
Intersection type створюється оператором &.
Значення intersection type повинно відповідати всім типам у перетині.
Перетини зручно використовувати для композиції властивостей об’єктів.
Intersection types можна створювати з type, interface та їх комбінацій.
Обов’язкові властивості всіх складових типів залишаються обов’язковими.
Несумісні типи однакових властивостей можуть утворити never.
& означає «і», а | означає «або».
Intersection type описує форму значення, але не об’єднує об’єкти автоматично під час виконання.