Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що означає "control flow based narrowing"?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Control flow-based narrowing** - це механізм TypeScript, який **аналізує потік виконання програми (control flow)** і **автоматично звужує тип змінних** залежно від логічних умов, перевірок і гілок коду. **Ключове:** TypeScript стежить за тим, які перевірки ти робиш, і розуміє, які типи залишаються можливими в кожній точці програми.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке Control Flow Based Narrowing > **Control flow-based narrowing** - це механізм TypeScript, > який **аналізує потік виконання програми (control flow)** > і **автоматично звужує тип змінних** залежно від логічних умов, перевірок і гілок коду. Простіше кажучи: > TypeScript стежить за тим, **які перевірки ти робиш**, > і "розуміє", **які типи залишаються можливими** в кожній точці програми. --- ## Приклад 1. Базовий приклад - `if` ```javascript function printId(id: string | number) { if (typeof id === "string") { // Тут TS бачить: id - точно string console.log(id.toUpperCase()); } else { // А тут: id - точно number console.log(id.toFixed(2)); } } ``` TypeScript аналізує **контроль потоку (**`if`**/**`else`**)**, і звузив тип `id` у кожній гілці. --- ## "Control flow" - це шлях виконання програми TypeScript усередині себе будує **граф потоку керування (control flow graph, CFG)**: - кожна гілка `if`, `else`, `return`, `throw`, `switch`, `try/catch` - це окрема "гілка" потоку; - у кожній гілці змінні можуть мати різні типи; - при поверненні в загальний блок TS "об'єднує" типи назад. --- ### Візуально: ```javascript let x: string | number; if (typeof x === "string") { // гілка 1 -> x: string } else { // гілка 2 -> x: number } // після if -> x: string | number (об'єднано) ``` --- ## Приклад 2. Перевірки null і undefined ```javascript function greet(name?: string | null) { if (!name) return; // відсікає null і undefined // Тут name: string console.log("Hello, " + name.toUpperCase()); } ``` TypeScript відстежує, що після `if (!name) return` можливі типи `null` і `undefined` **виключені** з об'єднання. --- ## Приклад 3. Звуження через `return` і `throw` TypeScript розуміє, що якщо виконання **переривається**, то гілка, що залишилася, має інший тип. ```javascript function ensureString(value: string | undefined): string { if (value === undefined) { throw new Error("Expected string"); } // Тут value: string (undefined виключено) return value.toUpperCase(); } ``` TS знає: "якщо `throw` не виконався, значить `value` точно не undefined". --- ## Приклад 4. Звуження через `in`, `instanceof`, `typeof` ```javascript type Cat = { meow: () => void }; type Dog = { bark: () => void }; function speak(pet: Cat | Dog) { if ("bark" in pet) { // pet: Dog pet.bark(); } else { // pet: Cat pet.meow(); } } ``` TypeScript використовує **перевірку властивості** як логічний доказ, щоб виключити невідповідний тип. --- ## Приклад 5. Комбінований потік звужень ```javascript function example(value: string | number | null) { if (value == null) { // value: null return; } if (typeof value === "string") { // value: string console.log(value.toUpperCase()); } else { // value: number console.log(value.toFixed(2)); } } ``` TS **крок за кроком** аналізує контроль потоку: ```javascript вихідний тип: string | number | null -> після if (value == null): null виключено -> після if (typeof value === "string"): number виключено ``` --- ## Приклад 6. Звуження при присвоєннях ```javascript let x: string | number = 42; if (typeof x === "number") { x = x + 1; // x: number } else { x = x.toUpperCase(); // x: string } ``` TypeScript навіть відстежує, **як змінюються типи після присвоєння**, це теж частина *control flow analysis*. --- ## Приклад 7. Звуження всередині тернарних операторів ```javascript function format(value: string | number) { return typeof value === "string" ? value.toUpperCase() : value.toFixed(2); } ``` TS звужує типи по кожній гілці тернарного виразу. --- ## Приклад 8. У зв'язці з `never` TypeScript може "зрозуміти", що **всі варіанти оброблено**, і тип, що залишився, стає `never`. ```javascript type Shape = | { kind: "circle"; r: number } | { kind: "square"; s: number }; function area(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.r ** 2; case "square": return shape.s ** 2; default: const _exhaustive: never = shape; // TS перевіряє, що сюди не потрапимо return _exhaustive; } } ``` Це приклад **дискримінованого звуження**, що ґрунтується на аналізі потоку (`switch`/`case`). --- ## Як TypeScript робить це "під капотом" 1. **Будує граф потоку керування (Control Flow Graph):** - кожна точка розгалуження (`if`, `return`, `throw`, `catch`) створює вузол. 2. **Присвоює кожному вузлу можливі типи.** 3. **Стежить за винятками і поверненнями**, де гілка завершується. 4. **Об'єднує типи назад**, коли потоки сходяться. Це й називається **control flow analysis**, аналіз типів *у контексті виконання програми*. --- ## Приклад 9. Навіть складні вирази TS розуміє ```javascript function test(a?: number, b?: number) { if (a && b) { // a: number, b: number } else if (a) { // a: number, b: undefined } else { // a: undefined, b: number | undefined } } ``` TS обчислює типи *в кожній гілці* залежно від того, які перевірки спрацювали. --- ## Приклад 10. Звуження після перевірки `try/catch` ```javascript function safeParse(input: string): object | null { try { return JSON.parse(input); } catch { return null; } } ``` TS знає: - усередині `try` результат `object`, - усередині `catch` - `null`. Це теж контроль потоку. --- ## Чому це важливо Без "control flow based narrowing" TypeScript був би просто "типовим валідатором", що не розуміє логіку програми. А з ним: - компілятор **аналізує реальні розгалуження і перевірки**; - типи автоматично **звужуються** там, де це логічно виправдано; - IDE показує **контекстно-залежні підказки**; - ти можеш **не боятися** `null`**,** `undefined`**,** `union` **і** `unknown` - TS захищає від помилок на рівні потоку виконання. --- ## Підсумок | Термін | Значення | |---|---| | **Control flow** | Потік виконання програми: `if`, `else`, `return`, `throw`, `switch`, `try` | | **Narrowing** | Звуження множини можливих типів до конкретного | | **Control flow-based narrowing** | Коли TS автоматично звужує типи, аналізуючи логіку і розгалуження коду | | **Головна ідея** | Тип змінної змінюється залежно від того, що "доведено" в поточній гілці | | **Результат** | Менше `as`, менше помилок, більше безпеки й автодоповнення |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.