Skip to main content

Безпека коду і narrowing

1. Запобігає зверненню до неіснуючих властивостей

Без narrowing TypeScript не може знати, які поля доступні, і міг би допустити небезпечне звернення.

javascript
function printId(id: string | number) { console.log(id.toUpperCase()); // Помилка: у number немає toUpperCase }

З narrowing:

javascript
function printId(id: string | number) { if (typeof id === "string") { console.log(id.toUpperCase()); // безпечно } else { console.log(id.toFixed(2)); // безпечно } }

Чому безпечно: TypeScript гарантує, що в першій гілці id - точно string, а в другій - number. Помилки несумісності будуть спіймані під час компіляції, а не в рантаймі.


2. Виключає "неможливі" сценарії виконання

Звуження типів дозволяє компілятору аналізувати потік керування і прибирати недосяжні гілки коду.

javascript
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. Робить код самодокументованим і очевидним

Звуження - це не лише про захист, а й про читабельність:

javascript
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

Звуження типів допомагає виключити такі ситуації ще до запуску:

javascript
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 не знає, який член об'єднання використовується.

javascript
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 тощо.

javascript
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 підказує лише ті методи, які реально існують у конкретного типу:

javascript
function log(x: string | Date) { if (x instanceof Date) { x.toISOString(); // підказка лише для Date } else { x.toUpperCase(); // підказка лише для string } }

Це знижує ризик випадкового виклику неіснуючого методу. Типи і автодоповнення стають контекстно-залежними.


8. Робить перевірки на повноту (exhaustiveness check)

Звуження дозволяє гарантувати, що ти обробив усі можливі варіанти типу.

javascript
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 вимагає звуження, перш ніж до нього можна звернутися. Це захищає від помилок "наосліп":

javascript
function process(value: unknown) { if (typeof value === "string") { console.log(value.toUpperCase()); // безпечно } else { // console.log(value.toUpperCase()); // помилка - не string } }

TypeScript не дозволить використати value, поки ти не доведеш, що він безпечного типу.


10. Захищає від неправильних приведень (as)

Без narrowing доводиться "насильно" приводити типи:

javascript
(user as { name: string }).name; // ризик - TS вірить на слово

З narrowing:

javascript
if (typeof user === "object" && user && "name" in user) { console.log(user.name); // безпечно, доведено логікою }

TypeScript не "вірить на слово" - він сам доводить коректність через умови.


Підсумок

Що дає narrowingЧому це безпечно
Виключає неможливі типиTS не дозволить викликати методи неіснуючого типу
Гарантує коректні властивостіЗвернення до об'єкта лише після перевірки
Запобігає помилкам null / undefinedКомпілятор знає, що значення вже не "порожнє"
Спрощує перевіркиIDE знає контекст і підказує правильні методи
Забезпечує вичерпну обробку union-типуНе можна забути жоден варіант
Мінімізує runtime-помилкиПомилки ловляться під час компіляції

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.