Пошук уроків, статей та іншого контенту
Розберіться, як Pick, Omit і Record вибирають, виключають та створюють набори властивостей.
Pick, Omit і RecordУ TypeScript часто потрібно створити новий тип на основі вже наявного:
залишити лише кілька властивостей;
виключити властивості, які не потрібні;
описати об’єкт із наперед відомими ключами та однаковим типом значень.
Для цього TypeScript має вбудовані utility types:
Pick — вибирає властивості;
Omit — виключає властивості;
Record — створює тип об’єкта із заданими ключами та типом значень.
Ці типи не змінюють об’єкти під час виконання програми. Вони працюють лише під час перевірки типів.
Pick: вибір властивостейСинтаксис:
Pick<Type, Keys>Pick створює новий тип, який містить лише вказані властивості з іншого типу.
interface User {
id: number;
name: string;
email: string;
age: number;
}
type UserPreview = Pick<User, "id" | "name">;Тепер UserPreview еквівалентний такому типу:
type UserPreview = {
id: number;
name: string;
};Використаємо його для функції, яка повертає скорочену інформацію про користувача:
interface User {
id: number;
name: string;
email: string;
age: number;
}
type UserPreview = Pick<User, "id" | "name">;
function getUserPreview(user: User): UserPreview {
return {
id: user.id,
name: user.name,
};
}
const user: User = {
id: 1,
name: "Олена",
email: "olena@example.com",
age: 28,
};
console.log(getUserPreview(user));Другий параметр Pick — це назви властивостей. TypeScript перевіряє, що вони справді існують у першому типі:
type UserPreview = Pick<User, "id" | "name">;
// Помилка: властивості "phone" немає в типі User
// type UserContacts = Pick<User, "email" | "phone">;Для вибору кількох властивостей використовується об’єднання типів через |:
type UserContacts = Pick<User, "email">;
type UserIdentity = Pick<User, "id" | "name">;Omit: виключення властивостейСинтаксис:
Omit<Type, Keys>Omit створює новий тип, у якому немає вказаних властивостей.
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Omit<User, "password">;PublicUser еквівалентний такому типу:
type PublicUser = {
id: number;
name: string;
email: string;
};Це зручно, коли потрібно передати дані користувача без приватного поля:
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Omit<User, "password">;
const publicUser: PublicUser = {
id: 1,
name: "Олена",
email: "olena@example.com",
};
console.log(publicUser);Omit також приймає кілька властивостей:
type UserWithoutPrivateData = Omit<User, "password" | "email">;У результаті залишаться лише id і name.
Pick і OmitОбидва типи створюють новий тип на основі іншого, але підхід у них різний.
PickВказуємо, що потрібно залишити:
type UserPreview = Pick<User, "id" | "name">;Це зручно, коли потрібні лише кілька властивостей.
OmitВказуємо, що потрібно прибрати:
type UserWithoutPassword = Omit<User, "password">;Це зручно, коли потрібно залишити більшість властивостей і виключити лише кілька.
Вибір залежить від того, який опис зрозуміліший у конкретній ситуації.
Record: створення типу об’єктаСинтаксис:
Record<Keys, ValueType>Record створює тип об’єкта, у якого:
ключі визначаються типом Keys;
значення всіх цих ключів мають тип ValueType.
Наприклад:
type UserRole = "admin" | "editor" | "viewer";
type RoleDescriptions = Record<UserRole, string>;Цей тип вимагає об’єкт із трьома ключами:
const roleDescriptions: RoleDescriptions = {
admin: "Має повний доступ",
editor: "Може редагувати дані",
viewer: "Може переглядати дані",
};Кожен ключ має бути присутнім:
type UserRole = "admin" | "editor" | "viewer";
type RoleDescriptions = Record<UserRole, string>;
// Помилка: відсутній ключ "viewer"
const descriptions: RoleDescriptions = {
admin: "Має повний доступ",
editor: "Може редагувати дані",
};Так само TypeScript перевіряє, щоб значення мали правильний тип:
type UserRole = "admin" | "editor" | "viewer";
type RoleDescriptions = Record<UserRole, string>;
const descriptions: RoleDescriptions = {
admin: "Має повний доступ",
editor: "Може редагувати дані",
viewer: "Може переглядати дані",
};
// Помилка: значення має бути рядком
// descriptions.admin = 123;Record із типом значень-об’єктівЗначеннями Record можуть бути не лише рядки чи числа, а й інші об’єкти.
type Status = "loading" | "success" | "error";
type StatusInfo = {
title: string;
color: string;
};
type StatusConfig = Record<Status, StatusInfo>;
const statusConfig: StatusConfig = {
loading: {
title: "Завантаження",
color: "yellow",
},
success: {
title: "Успішно",
color: "green",
},
error: {
title: "Помилка",
color: "red",
},
};
console.log(statusConfig.success.title);Такий підхід корисний для словників, конфігурацій і відповідностей між ключами та значеннями.
Record із загальним ключемКлючами можуть бути всі рядкові значення:
type Scores = Record<string, number>;
const scores: Scores = {
math: 95,
english: 88,
history: 91,
};У цьому випадку об’єкт може мати будь-які рядкові ключі, але значення кожного ключа повинно бути числом.
Також можна використовувати числа:
type UserNames = Record<number, string>;
const userNames: UserNames = {
1: "Олена",
2: "Андрій",
};На практиці для обмеженого набору ключів краще використовувати об’єднання рядкових літералів:
type Language = "uk" | "en" | "pl";
type LanguageNames = Record<Language, string>;
const languageNames: LanguageNames = {
uk: "Українська",
en: "English",
pl: "Polski",
};Так TypeScript не дозволить випадково пропустити потрібну мову або додати ключ з помилкою.
Pick, Omit і Record можна використовувати разом.
Наприклад, спочатку створимо публічний тип користувача без пароля, а потім опишемо об’єкт користувачів за їхніми ідентифікаторами:
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Omit<User, "password">;
type UsersById = Record<number, PublicUser>;
const usersById: UsersById = {
1: {
id: 1,
name: "Олена",
email: "olena@example.com",
},
2: {
id: 2,
name: "Андрій",
email: "andrii@example.com",
},
};
console.log(usersById[1].name);У цьому прикладі:
Omit<User, "password"> прибирає пароль.
Record<number, PublicUser> створює об’єкт, де ключами є ідентифікатори, а значеннями — публічні дані користувачів.
Pick і Omit не видаляють властивості з реального об’єкта. Вони лише створюють інший тип для перевірки.
interface User {
id: number;
name: string;
password: string;
}
type PublicUser = Omit<User, "password">;
const user: User = {
id: 1,
name: "Олена",
password: "secret",
};
const publicUser: PublicUser = {
id: user.id,
name: user.name,
};
console.log(user.password);У вихідному об’єкті user поле password нікуди не зникає. Якщо потрібно справді не передавати його далі, об’єкт треба створити або перетворити відповідним чином.
Pickinterface Product {
id: number;
title: string;
}
// Помилка: "price" не є властивістю Product
// type ProductInfo = Pick<Product, "title" | "price">;Перевірте назви властивостей у вихідному типі.
OmitOmit виключає названі властивості, а всі інші залишає:
interface Product {
id: number;
title: string;
price: number;
}
type ProductWithoutId = Omit<Product, "id">;ProductWithoutId усе ще містить title і price.
RecordЯкщо ключі задані об’єднанням літералів, кожен із них обов’язковий:
type Theme = "light" | "dark";
type ThemeLabels = Record<Theme, string>;
// Помилка: відсутній ключ "dark"
// const labels: ThemeLabels = {
// light: "Світла тема",
// };Потрібно додати всі ключі:
const labels: ThemeLabels = {
light: "Світла тема",
dark: "Темна тема",
};Recordtype Prices = Record<string, number>;
// Помилка: значення має бути числом
// const prices: Prices = {
// book: "100",
// };Record перевіряє тип кожного значення, а не лише структуру об’єкта загалом.
Record і звичайним типом об’єктаЯкщо набір ключів відомий, Record допомагає не пропустити жоден із них:
type Permission = "read" | "write" | "delete";
type Permissions = Record<Permission, boolean>;Звичайний індексний тип на кшталт Record<string, boolean> дозволив би довільні ключі й не вимагав би саме read, write та delete.
Використовуйте Pick, коли потрібно залишити невелику відому групу властивостей.
Використовуйте Omit, коли потрібно взяти майже весь тип і прибрати кілька властивостей.
Використовуйте Record, коли потрібно описати об’єкт як набір ключів одного типу зі значеннями іншого типу.
Приклад вибору:
interface Article {
id: number;
title: string;
text: string;
author: string;
internalNotes: string;
}
type ArticleCard = Pick<Article, "id" | "title" | "author">;
type PublicArticle = Omit<Article, "internalNotes">;
type ArticlesById = Record<number, PublicArticle>;Pick<Type, Keys> створює тип лише з вибраних властивостей.
Omit<Type, Keys> створює тип без указаних властивостей.
Record<Keys, ValueType> створює тип об’єкта з визначеними ключами та типом їхніх значень.
Для кількох властивостей або ключів використовується об’єднання через |.
Ці utility types працюють під час перевірки TypeScript і не змінюють об’єкти під час виконання.
Pick, Omit і Record можна поєднувати для побудови точних і зрозумілих типів.