Пошук уроків, статей та іншого контенту
Застосовуйте порівняння значень, null-перевірки та truthiness для безпечного звуження типів.
Звуження типів — це процес, під час якого TypeScript використовує умови в коді, щоб визначити точніший тип значення.
Наприклад, змінна може мати тип string | number. Поки TypeScript не знає, яке саме значення зберігається у змінній, не можна безпечно викликати методи рядка або виконувати числові операції:
function formatValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}Умова typeof value === "string" звузила тип value до string усередині першої гілки. В іншій гілці TypeScript знає, що value має тип number.
У цьому уроці розглянемо звуження за допомогою:
строгого порівняння значень;
перевірки на null та undefined;
truthiness — перевірки, чи є значення істинним або хибним у логічному контексті.
TypeScript може звужувати тип, коли ми порівнюємо значення за допомогою === або !==.
function printId(id: string | number): void {
if (typeof id === "string") {
console.log(`Текстовий ідентифікатор: ${id.toUpperCase()}`);
} else {
console.log(`Числовий ідентифікатор: ${id.toFixed(0)}`);
}
}
printId("user-42");
printId(42);Порівняння typeof id === "string" повідомляє TypeScript, що в цій гілці id — рядок.
Так само можна порівнювати значення безпосередньо:
function getStatusMessage(status: "success" | "error" | "loading"): string {
if (status === "success") {
return "Операцію виконано успішно";
}
if (status === "error") {
return "Сталася помилка";
}
return "Операція виконується";
}
console.log(getStatusMessage("success"));
console.log(getStatusMessage("loading"));Після перевірки status === "success" TypeScript знає, що в цій гілці значення має конкретний тип-літерал "success".
Якщо дві змінні мають спільні можливі типи, їхнє порівняння також може звузити типи:
function compareValues(
first: string | number,
second: string | boolean
): void {
if (first === second) {
console.log("Значення однакові");
// У цій гілці обидва значення можуть бути лише string.
console.log(first.toUpperCase());
console.log(second.toUpperCase());
} else {
console.log("Значення різні");
}
}
compareValues("hello", "hello");
compareValues(10, true);Єдиний тип, який може бути одночасно у first і second, — string. Тому після first === second TypeScript дозволяє використовувати методи рядка для обох змінних.
null та undefinedЗначення часто можуть бути відсутніми. Це явно описують об’єднанням типів:
function getUsername(): string | null {
return Math.random() > 0.5 ? "Olena" : null;
}Поки результат має тип string | null, не можна без перевірки викликати рядкові методи:
const username = getUsername();
// Помилка: username може бути null
// console.log(username.toUpperCase());Спочатку потрібно перевірити, що значення не дорівнює null:
function printUsername(username: string | null): void {
if (username !== null) {
console.log(username.toUpperCase());
} else {
console.log("Ім'я користувача відсутнє");
}
}
printUsername("Olena");
printUsername(null);У гілці if тип username звужено до string.
undefinedТак само перевіряють значення, яке може бути undefined:
function findUserEmail(userId: number): string | undefined {
if (userId === 1) {
return "user@example.com";
}
return undefined;
}
function printEmail(userId: number): void {
const email = findUserEmail(userId);
if (email !== undefined) {
console.log(email.toLowerCase());
} else {
console.log("Електронну адресу не знайдено");
}
}
printEmail(1);
printEmail(2);Після перевірки email !== undefined TypeScript знає, що email — це string.
null і undefined одночасноЯкщо значення може бути і null, і undefined, можна перевірити обидва варіанти:
function printDescription(description: string | null | undefined): void {
if (description !== null && description !== undefined) {
console.log(description.trim());
} else {
console.log("Опис відсутній");
}
}
printDescription(" Текст опису ");
printDescription(null);
printDescription(undefined);Умови з && гарантують, що всередині блоку значення не є ні null, ні undefined.
!= nullІснує короткий запис:
function printTitle(title: string | null | undefined): void {
if (title != null) {
console.log(title.trim());
} else {
console.log("Заголовок відсутній");
}
}Перевірка title != null спеціально корисна для значень, які можуть бути null або undefined. Вона відкидає обидва варіанти:
null;
undefined.
Водночас значення "", 0 і false проходять цю перевірку, оскільки вони не є null або undefined.
Для інших порівнянь зазвичай варто використовувати строгі оператори === та !==.
У JavaScript значення автоматично трактуються як істинні або хибні в умовах if, while та інших логічних виразах.
До хибних значень належать:
false;
0;
-0;
0n;
"";
null;
undefined;
NaN.
Усі інші значення є істинними.
TypeScript використовує цю поведінку для звуження типів:
function printMessage(message: string | null): void {
if (message) {
console.log(message.toUpperCase());
} else {
console.log("Повідомлення відсутнє або порожнє");
}
}
printMessage("Привіт");
printMessage(null);
printMessage("");У гілці if TypeScript знає, що message — непорожній string. Значення null відсіюється.
Truthiness-перевірка особливо зручна, коли потрібно пропустити порожні значення:
function getDisplayName(
firstName: string | undefined,
lastName: string | undefined
): string {
if (firstName && lastName) {
return `${firstName} ${lastName}`;
}
return firstName || lastName || "Невідомий користувач";
}
console.log(getDisplayName("Олена", "Коваль"));
console.log(getDisplayName("Олена", undefined));
console.log(getDisplayName(undefined, undefined));Умова if (firstName && lastName) виконується лише тоді, коли обидва рядки непорожні.
Truthiness-перевірка відкидає не лише null та undefined, а й інші хибні значення. Тому вона може дати неочікуваний результат для чисел, булевих значень або порожніх рядків.
function printCount(count: number | undefined): void {
if (count) {
console.log(`Кількість: ${count}`);
} else {
console.log("Кількість відсутня");
}
}
printCount(5);
printCount(0);Для printCount(0) виконається гілка else, хоча 0 — це коректна кількість. Якщо потрібно перевірити саме наявність значення, використовуйте явне порівняння:
function printCountSafely(count: number | undefined): void {
if (count !== undefined) {
console.log(`Кількість: ${count}`);
} else {
console.log("Кількість відсутня");
}
}
printCountSafely(0);
printCountSafely(5);
printCountSafely(undefined);Так само порожній рядок може бути коректним значенням:
function printSearchQuery(query: string | undefined): void {
if (query !== undefined) {
console.log(`Запит: "${query}"`);
} else {
console.log("Запит не передано");
}
}
printSearchQuery("");
printSearchQuery(undefined);Якщо потрібно відкинути і undefined, і порожній рядок, truthiness-перевірка підходить:
function search(query: string | undefined): void {
if (!query) {
console.log("Пошуковий запит порожній");
return;
}
console.log(`Пошук за запитом: ${query}`);
}
search("TypeScript");
search("");
search(undefined);Вибір перевірки залежить від потрібної логіки:
if (value) — значення має бути truthy;
if (value !== undefined) — значення не повинно бути undefined;
if (value !== null) — значення не повинно бути null;
if (value != null) — значення не повинно бути ні null, ні undefined.
type User = {
name: string;
age: number;
email?: string;
};
function describeUser(user: User | null): string {
if (user === null) {
return "Користувача не знайдено";
}
const emailMessage = user.email
? `Електронна адреса: ${user.email}`
: "Електронну адресу не вказано";
return `${user.name}, ${user.age} років. ${emailMessage}`;
}
function canVote(age: number | undefined): boolean {
if (age === undefined) {
return false;
}
return age >= 18;
}
function formatInput(input: string | number | null): string {
if (input === null) {
return "Значення відсутнє";
}
if (typeof input === "string") {
if (!input) {
return "Рядок порожній";
}
return `Рядок: ${input.toUpperCase()}`;
}
return `Число: ${input.toFixed(2)}`;
}
console.log(
describeUser({
name: "Олена",
age: 25,
email: "olena@example.com",
})
);
console.log(describeUser(null));
console.log(canVote(20));
console.log(canVote(undefined));
console.log(formatInput("hello"));
console.log(formatInput(""));
console.log(formatInput(12.5));
console.log(formatInput(null));У цьому прикладі використано кілька видів звуження:
user === null звужує User | null до User;
user.email ? ... : ... перевіряє наявність рядка;
age === undefined звужує number | undefined до number;
input === null відсіює null;
typeof input === "string" відрізняє рядок від числа;
if (!input) перевіряє, чи є рядок порожнім.
function showPage(page: number | undefined): void {
if (page) {
console.log(`Сторінка ${page}`);
}
}Для page = 0 нічого не буде виведено. Якщо нуль допустимий, краще написати:
function showPage(page: number | undefined): void {
if (page !== undefined) {
console.log(`Сторінка ${page}`);
}
}function normalizeName(name: string | null): string {
// Помилка: name може бути null
// return name.trim().toLowerCase();
if (name === null) {
return "";
}
return name.trim().toLowerCase();
}Спочатку потрібно перевірити потенційно відсутнє значення, а вже потім використовувати його як рядок.
null і undefinedПеревірка лише на null не відкидає undefined:
function printValue(value: string | null | undefined): void {
if (value !== null) {
// Тут value все ще може бути undefined
// console.log(value.toUpperCase());
return;
}
console.log("Значення дорівнює null");
}Якщо потрібно обробити обидва варіанти, використовуйте:
function printValueSafely(value: string | null | undefined): void {
if (value != null) {
console.log(value.toUpperCase());
}
}Порівняння == виконує неявне приведення типів і може приховувати помилки:
console.log(0 == false); // true
console.log("" == false); // trueДля звичайних порівнянь використовуйте === та !==. Винятком може бути цілеспрямована перевірка value != null, коли потрібно одночасно відкинути null і undefined.
TypeScript звужує типи після перевірок у if та інших умовах.
Порівняння === і !== допомагають визначити конкретний тип або значення.
Перевірка value !== null відкидає null.
Перевірка value !== undefined відкидає undefined.
Перевірка value != null відкидає одночасно null та undefined.
Truthiness-перевірка if (value) відкидає всі хибні значення, зокрема 0, "", false, null і undefined.
Якщо 0 або порожній рядок є допустимими значеннями, не замінюйте явну перевірку truthiness.
Спочатку звужуйте тип, а вже потім викликайте методи або виконуйте операції над значенням.