Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке type narrowing у TypeScript?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Type narrowing** (звуження типу) - це процес, під час якого TypeScript уточнює тип змінної на основі логічних перевірок, операторів та контексту виконання коду, спостерігаючи за `if`, `typeof`, `in`, `instanceof` та іншими перевірками. **Ключове:** коли TypeScript виключає всі можливі варіанти, змінна отримує тип `never` - це основа для discriminated unions і безпечних `if`/`switch` без зайвих кастів.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке Type Narrowing **Type narrowing** (звуження типу) - це процес, під час якого TypeScript **уточнює** тип змінної на основі логічних перевірок, операторів та контексту виконання коду. > Простіше кажучи: > TypeScript "спостерігає" за твоїми `if`, `typeof`, `in`, `instanceof` та іншими перевірками - > і **розуміє**, який тип реально можливий у кожній гілці коду. --- ### Приклад: без narrowing ```javascript function printId(id: string | number) { console.log(id.toUpperCase()); // Помилка - id може бути number } ``` `id` має тип `string | number`, і TypeScript не знає, який із них використовується зараз. --- ### Приклад з narrowing ```javascript function printId(id: string | number) { if (typeof id === "string") { // Тут id: string console.log(id.toUpperCase()); } else { // Тут id: number console.log(id.toFixed(2)); } } ``` TypeScript **звузив** тип змінної `id`: у першій гілці `string`, у другій - `number`. --- ## Як працює narrowing TypeScript аналізує **контроль потоку** (control flow analysis) і автоматично "відсікає" неможливі типи, коли зустрічає: - перевірки `typeof`, `instanceof`, `in`, `===`, `!==`, `==`, `!=`; - перевірки на `null` / `undefined`; - користувацькі **type guards** (`value is Type`); - логічні вирази (`if`, `else`, `switch`, `return`, `throw` тощо). --- ## Основні способи звуження типів | Спосіб | Опис | Приклад | |---|---|---| | `typeof` | Для примітивів | `typeof x === "string"` | | `instanceof` | Для класів і конструкторів | `x instanceof Date` | | `in` | Перевіряє наявність властивості | `"prop" in obj` | | `===` / `!==` | Порівняння літеральних значень | `x === null` | | Перевірка `null` / `undefined` | Виключає "порожні" значення | `if (x)` | | Користувацький type guard | Функція вигляду `x is T` | `isArray(value): value is any[]` | --- ## Приклади різних варіантів narrowing ### 1. `typeof` - примітиви ```javascript function log(value: string | number | boolean) { if (typeof value === "string") { value.toUpperCase(); // string } else if (typeof value === "number") { value.toFixed(2); // number } else { value.valueOf(); // boolean } } ``` --- ### 2. `instanceof` - класи та об'єкти ```javascript function logDate(d: Date | string) { if (d instanceof Date) { d.toISOString(); // Date } else { d.toUpperCase(); // string } } ``` --- ### 3. `in` - перевірка наявності властивості ```javascript type Dog = { bark: () => void }; type Cat = { meow: () => void }; function speak(animal: Dog | Cat) { if ("bark" in animal) { animal.bark(); // Dog } else { animal.meow(); // Cat } } ``` --- ### 4. Перевірки `== null` ```javascript function greet(name?: string | null) { if (name == null) { console.log("No name provided"); } else { console.log("Hello, " + name.toUpperCase()); } } ``` Після `if (name == null)` TypeScript звужує тип до `null | undefined`, а після `else` - до `string`. --- ### 5. З користувацьким type guard ```javascript function isString(value: unknown): value is string { return typeof value === "string"; } function print(value: unknown) { if (isString(value)) { value.toUpperCase(); // тепер точно string } } ``` > Користувацькі type guards - це спосіб навчити TS "розпізнавати" твої власні типи. --- ### 6. З літеральними типами та порівняннями ```javascript type Status = "loading" | "success" | "error"; function handle(status: Status) { if (status === "loading") { // status: "loading" } else if (status === "success") { // status: "success" } else { // status: "error" } } ``` Це окремий випадок **discriminated union narrowing** (звуження за дискримінуючою властивістю). --- ## Звуження в об'єднаних типах (Union Narrowing) Якщо змінна має тип `A | B | C`, TypeScript **звузить** його до конкретного підтипу, коли умова виключить решту. ```javascript function example(x: string | number | boolean) { if (typeof x === "string") { // string } else if (typeof x === "number") { // number } else { // boolean } } ``` --- ## Звуження з `never` Коли TypeScript виключає всі можливі варіанти, змінна отримує тип `never` (недосяжний тип). ```javascript function process(value: string | number) { if (typeof value === "string") { return value.toUpperCase(); } else if (typeof value === "number") { return value.toFixed(2); } else { // value: never (сюди потрапити не можна) throw new Error("Unexpected type"); } } ``` --- ## Чому це важливо **Безпека:** TS не дасть викликати метод, який не належить типу. **Зручність:** IDE підказує доступні властивості й методи після narrowing. **Ясність коду:** Можна писати чисті `if`/`switch`, без зайвих кастів (`as`). **Основа discriminated unions:** Без narrowing TS не зміг би розуміти, який варіант union-типу використовується. --- ## Підсумок | Поняття | Опис | |---|---| | **Type narrowing** | Процес уточнення типу змінної на основі умов | | **Навіщо потрібно** | Щоб TypeScript знав, який підтип реально використовується в конкретному місці | | **Коли спрацьовує** | При перевірках (`typeof`, `instanceof`, `in`, `===`, `is`, `null` тощо) | | **Результат** | TS "звужує" union або unknown тип до конкретного | | **Тип "never"** | Використовується, коли інших варіантів не залишається | --- **Просто запам'ятай:** > *Type narrowing* - це коли TypeScript "думає як людина": > він читає твій `if` і **розуміє**, який тип значення всередині кожної гілки.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.