Пошук уроків, статей та іншого контенту
Розберете оператор keyof, отримання об’єднання ключів і безпечний доступ до властивостей об’єктних типів.
keyofkeyof — це оператор TypeScript, який отримує ключі об’єктного типу й створює з них об’єднання рядкових, числових або символьних літералів.
interface User {
id: number;
name: string;
isActive: boolean;
}
type UserKey = keyof User;
// "id" | "name" | "isActive"Тип UserKey може містити лише ключі, які описані в User:
let key: UserKey;
key = "id";
key = "name";
key = "isActive";
// Помилка: такого ключа немає в типі User
// key = "email";Отже, keyof User не є звичайним типом string. Це конкретне об’єднання дозволених ключів:
type UserKey = "id" | "name" | "isActive";Але на практиці не потрібно створювати таке об’єднання вручну. TypeScript може отримати його за допомогою keyof.
Якщо ключ зберігається у змінній, TypeScript має переконатися, що цей ключ справді існує в об’єкті.
interface User {
id: number;
name: string;
isActive: boolean;
}
const user: User = {
id: 1,
name: "Олена",
isActive: true,
};
const key: keyof User = "name";
const value = user[key];
console.log(value); // ОленаТип змінної key — не довільний string, а "id" | "name" | "isActive". Тому TypeScript дозволяє безпечний доступ до user[key].
Оскільки ключ може бути будь-яким із трьох, тип value буде об’єднанням типів усіх властивостей:
// typeof value:
// number | string | booleanTypeScript не може заздалегідь знати, яке саме значення буде в key під час виконання програми.
keyof часто використовують разом із узагальненими типами.
interface User {
id: number;
name: string;
isActive: boolean;
}
function getProperty<T, K extends keyof T>(object: T, key: K): T[K] {
return object[key];
}
const user: User = {
id: 1,
name: "Олена",
isActive: true,
};
const userName = getProperty(user, "name");
const userId = getProperty(user, "id");
const userStatus = getProperty(user, "isActive");
console.log(userName); // Олена
console.log(userId); // 1
console.log(userStatus); // trueРозглянемо параметри функції:
T — тип переданого об’єкта;
K extends keyof T — K може бути лише ключем цього об’єкта;
T[K] — тип значення властивості за ключем K.
TypeScript зберігає зв’язок між ключем і значенням:
const name = getProperty(user, "name");
// string
const id = getProperty(user, "id");
// number
const isActive = getProperty(user, "isActive");
// booleanВиклик із неіснуючим ключем спричинить помилку:
// Помилка: "email" не є ключем типу User
// const email = getProperty(user, "email");Це безпечніше, ніж функція, яка приймає будь-який рядок:
function getPropertyUnsafe(object: User, key: string) {
// TypeScript не може гарантувати, що key є ключем User
// return object[key];
}keyof typeof для звичайного об’єктаkeyof працює з типами, а не безпосередньо зі значеннями. Якщо потрібно отримати ключі вже створеного об’єкта, використовуйте typeof:
const settings = {
theme: "dark",
language: "uk",
notifications: true,
};
type SettingKey = keyof typeof settings;
// "theme" | "language" | "notifications"
function readSetting(key: SettingKey) {
return settings[key];
}
console.log(readSetting("theme")); // dark
console.log(readSetting("language")); // uk
// Помилка: "fontSize" не є ключем об’єкта settings
// readSetting("fontSize");Тут:
typeof settings отримує тип змінної settings;
keyof typeof settings отримує ключі цього типу.
keyofЯкщо додати до типу нову властивість, результат keyof автоматично зміниться:
interface Product {
id: number;
title: string;
price: number;
}
type ProductKey = keyof Product;
// "id" | "title" | "price"Якщо додати властивість inStock, тип ProductKey також міститиме "inStock". Це зменшує дублювання типів і допомагає підтримувати код.
keyof та обмежені ключіМожна дозволити функції працювати лише з певними ключами:
interface Article {
title: string;
author: string;
views: number;
published: boolean;
}
type TextKey = "title" | "author";
function printText(article: Article, key: TextKey): void {
console.log(article[key]);
}
const article: Article = {
title: "TypeScript для початківців",
author: "Ірина",
views: 120,
published: true,
};
printText(article, "title");
printText(article, "author");
// Помилка: "views" не належить до TextKey
// printText(article, "views");Якщо потрібно дозволити всі ключі Article, можна використати keyof Article:
function printValue(article: Article, key: keyof Article): void {
console.log(article[key]);
}
printValue(article, "title");
printValue(article, "views");
printValue(article, "published");keyof зі значеннямkeyof застосовується до типу:
interface User {
id: number;
name: string;
}
type UserKey = keyof User;Для значення спочатку потрібно отримати його тип через typeof:
const user = {
id: 1,
name: "Олена",
};
type UserKey = keyof typeof user;stringТакий параметр занадто широкий:
function readUser(user: User, key: string) {
// key може містити будь-який рядок
}Краще обмежити його:
function readUser(user: User, key: keyof User) {
return user[key];
}Тепер TypeScript не дозволить передати ключ, якого немає в User.
keyof User описує ключі:
type UserKey = keyof User;
// "id" | "name"А User[keyof User] описує можливі значення властивостей:
interface User {
id: number;
name: string;
}
type UserValue = User[keyof User];
// number | stringkeyof Type отримує об’єднання ключів об’єктного типу.
Результат keyof User може бути типом на кшталт "id" | "name" | "isActive".
keyof допомагає безпечно звертатися до властивостей через змінний ключ.
Для об’єкта-значення використовуйте keyof typeof object.
Запис K extends keyof T обмежує параметр функції ключами переданого об’єкта.
Запис T[K] отримує тип значення властивості за ключем K.