Що таке type narrowing у TypeScript?
Що таке Type Narrowing
Type narrowing (звуження типу) - це процес, під час якого TypeScript уточнює тип змінної на основі логічних перевірок, операторів та контексту виконання коду.
Простіше кажучи: TypeScript "спостерігає" за твоїми
if,typeof,in,instanceofта іншими перевірками - і розуміє, який тип реально можливий у кожній гілці коду.
Приклад: без narrowing
function printId(id: string | number) {
console.log(id.toUpperCase()); // Помилка - id може бути number
}id має тип string | number,
і TypeScript не знає, який із них використовується зараз.
Приклад з narrowing
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 - примітиви
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 - класи та об'єкти
function logDate(d: Date | string) {
if (d instanceof Date) {
d.toISOString(); // Date
} else {
d.toUpperCase(); // string
}
}3. in - перевірка наявності властивості
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
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
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. З літеральними типами та порівняннями
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 звузить його до конкретного підтипу,
коли умова виключить решту.
function example(x: string | number | boolean) {
if (typeof x === "string") {
// string
} else if (typeof x === "number") {
// number
} else {
// boolean
}
}Звуження з never
Коли TypeScript виключає всі можливі варіанти,
змінна отримує тип never (недосяжний тип).
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і розуміє, який тип значення всередині кожної гілки.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.