Безпека коду і narrowing
1. Запобігає зверненню до неіснуючих властивостей
Без narrowing TypeScript не може знати, які поля доступні, і міг би допустити небезпечне звернення.
function printId(id: string | number) {
console.log(id.toUpperCase()); // Помилка: у number немає toUpperCase
}З narrowing:
function printId(id: string | number) {
if (typeof id === "string") {
console.log(id.toUpperCase()); // безпечно
} else {
console.log(id.toFixed(2)); // безпечно
}
}Чому безпечно:
TypeScript гарантує, що в першій гілці id - точно string,
а в другій - number.
Помилки несумісності будуть спіймані під час компіляції, а не в рантаймі.
2. Виключає "неможливі" сценарії виконання
Звуження типів дозволяє компілятору аналізувати потік керування і прибирати недосяжні гілки коду.
function process(value: string | null) {
if (value === null) return;
// Тут value гарантовано string
console.log(value.toUpperCase()); // безпечно
}Без перевірки if (value === null), виклик toUpperCase() міг би призвести до помилки під час виконання (Cannot read properties of null).
3. Робить код самодокументованим і очевидним
Звуження - це не лише про захист, а й про читабельність:
type Response =
| { status: "success"; data: string }
| { status: "error"; message: string };
function handle(res: Response) {
if (res.status === "success") {
console.log(res.data); // безпечно
} else {
console.log(res.message); // безпечно
}
}TypeScript використовує дискримінатор status,
розуміє, який варіант активний,
і підказує лише допустимі властивості в IDE.
Перевага:
жодних "випадкових" звернень до res.data у хибній гілці.
IDE просто не дасть це зробити.
4. Знижує ймовірність помилок undefined і null
Одні з найчастіших рантайм-помилок у JS:
Cannot read property 'x' of undefined
Звуження типів допомагає виключити такі ситуації ще до запуску:
function showUser(user?: { name: string } | null) {
if (!user) return; // відсікаємо undefined і null
console.log(user.name); // точно існує
}TS "бачить", що після if (!user) return
змінна user не може бути undefined | null.
А отже, доступ до user.name безпечний.
5. Підвищує безпеку при роботі з union типами
Без narrowing TypeScript не знає, який член об'єднання використовується.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2; // безпечно
} else {
return shape.side ** 2; // безпечно
}
}Якби TS не звужував тип за kind,
виклик shape.radius міг би звернутися до поля, якого немає у square,
що призвело б до помилки в рантаймі.
6. Допомагає при логічних розгалуженнях і винятках
TypeScript вміє розуміти, які типи виключені після throw, return, break тощо.
function getLength(value: string | null) {
if (value === null) throw new Error("Empty value");
// Тут value точно string
return value.length; // безпечно
}Завдяки narrowing, після throw TypeScript знає: value - не null.
7. Допомагає IDE підказувати коректні методи і властивості
Коли тип звужений, IDE підказує лише ті методи, які реально існують у конкретного типу:
function log(x: string | Date) {
if (x instanceof Date) {
x.toISOString(); // підказка лише для Date
} else {
x.toUpperCase(); // підказка лише для string
}
}Це знижує ризик випадкового виклику неіснуючого методу. Типи і автодоповнення стають контекстно-залежними.
8. Робить перевірки на повноту (exhaustiveness check)
Звуження дозволяє гарантувати, що ти обробив усі можливі варіанти типу.
type Status = "loading" | "success" | "error";
function handle(status: Status) {
switch (status) {
case "loading":
break;
case "success":
break;
case "error":
break;
default:
const _exhaustive: never = status; // Помилка, якщо з'явиться новий статус
}
}TypeScript гарантує, що жоден варіант не пропущений. Це абсолютний захист від логічних помилок.
9. Допомагає безпечно працювати з unknown
Тип unknown вимагає звуження, перш ніж до нього можна звернутися.
Це захищає від помилок "наосліп":
function process(value: unknown) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // безпечно
} else {
// console.log(value.toUpperCase()); // помилка - не string
}
}TypeScript не дозволить використати value,
поки ти не доведеш, що він безпечного типу.
10. Захищає від неправильних приведень (as)
Без narrowing доводиться "насильно" приводити типи:
(user as { name: string }).name; // ризик - TS вірить на словоЗ narrowing:
if (typeof user === "object" && user && "name" in user) {
console.log(user.name); // безпечно, доведено логікою
}TypeScript не "вірить на слово" - він сам доводить коректність через умови.
Підсумок
| Що дає narrowing | Чому це безпечно |
|---|---|
| Виключає неможливі типи | TS не дозволить викликати методи неіснуючого типу |
| Гарантує коректні властивості | Звернення до об'єкта лише після перевірки |
Запобігає помилкам null / undefined | Компілятор знає, що значення вже не "порожнє" |
| Спрощує перевірки | IDE знає контекст і підказує правильні методи |
| Забезпечує вичерпну обробку union-типу | Не можна забути жоден варіант |
| Мінімізує runtime-помилки | Помилки ловляться під час компіляції |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.