Що таке type guard function?
Що таке Type Guard Function
Type guard function - це спеціальна функція, яка повертає логічне значення і повідомляє TypeScript, що якщо вона повернула
true, значить її аргумент має конкретний тип.
Синтаксис:
function isSomething(value: unknown): value is SomeType {
// логіка перевірки
}Ключова частина - це тип, що повертається:
value is SomeType -> type predicate (твердження про тип).
Приклад 1. Базовий кастомний type guard
function isString(value: unknown): value is string {
return typeof value === "string";
}
function print(value: unknown) {
if (isString(value)) {
// тут value: string
console.log(value.toUpperCase());
} else {
// value не string
console.log("Not a string");
}
}Завдяки value is string,
TypeScript розуміє, що всередині if (isString(value))
тип змінної звузився до string.
Навіщо потрібні Type Guard Functions
- Щоб розширити систему типів TypeScript - навчити її розуміти твої перевірки.
- Щоб уникнути примусових приведень (
as). - Щоб безпечно працювати з
unknown,anyчи union-типами. - Щоб звужувати типи всередині логічних умов, фільтрів і функцій вищого порядку.
Приклад 2. Перевірка масиву
function isArray(value: unknown): value is any[] {
return Array.isArray(value);
}
function process(value: unknown) {
if (isArray(value)) {
// value: any[]
console.log(value.length);
} else {
console.log("Not an array");
}
}Як це працює під капотом
Звичайна функція повертає boolean:
function check(value: unknown): booleanTypeScript не може з цього вивести тип.
А функція з type predicate (value is Type) каже компілятору:
"Якщо я повернув true, вважай, що змінна тепер має тип
Type".
Приклад 3. З union типом
type Dog = { kind: "dog"; bark: () => void };
type Cat = { kind: "cat"; meow: () => void };
function isDog(animal: Dog | Cat): animal is Dog {
return animal.kind === "dog";
}
function makeSound(animal: Dog | Cat) {
if (isDog(animal)) {
animal.bark(); // animal: Dog
} else {
animal.meow(); // animal: Cat
}
}Це типовий приклад type guard + narrowing:
функція isDog звужує union-тип Dog | Cat до конкретного підтипу.
Де часто використовується
| Сценарій | Приклад |
|---|---|
| Перевірка типу аргументу | isString(x) |
| Перевірка на null/undefined | isNonNull(x) |
| Перевірка дискримінатора | isError(response) |
| Перевірка instanceof | isDate(obj) |
| Фільтрація масиву | arr.filter(isString) |
Приклад 4. Фільтрація масиву з Type Guard
function isString(value: unknown): value is string {
return typeof value === "string";
}
const mixed = [1, "a", 2, "b", true];
const strings = mixed.filter(isString);
// strings: string[]TypeScript знає, що після фільтра залишилися лише string.
Приклад 5. Перевірка складних структур
type User = { id: number; name: string };
function isUser(obj: any): obj is User {
return (
typeof obj === "object" &&
obj !== null &&
typeof obj.id === "number" &&
typeof obj.name === "string"
);
}
const data: unknown = JSON.parse('{"id":1,"name":"Tom"}');
if (isUser(data)) {
// data: User
console.log(data.name.toUpperCase());
}Без type guard'а довелося б писати:
const user = data as User; // небезпечно, може впасти в рантайміПриклад 6. Перевірка на існування ключа
function hasProperty<T extends object, K extends PropertyKey>(
obj: T,
key: K
): obj is T & Record<K, unknown> {
return key in obj;
}
const person = { name: "Alex" };
if (hasProperty(person, "name")) {
// person: { name: string } & Record<"name", unknown>
console.log(person.name);
}Приклад 7. Композиція декількох type guards
function isDefined<T>(value: T | undefined | null): value is T {
return value != null;
}
function isPositive(n: number): boolean {
return n > 0;
}
const arr = [1, null, 2, undefined, -5, 3];
const positive = arr.filter(isDefined).filter(isPositive);
// positive: number[]Після isDefined TypeScript знає, що в масиві більше немає null чи undefined.
Відмінність Type Guard Function від звичайної функції
| Звичайна функція | Type Guard Function |
|---|---|
Повертає boolean | Повертає value is Type |
| Не змінює тип змінної | Звужує тип змінної |
| Не впливає на компілятор | Змінює сприйняття типу TS |
| Використовується як логічна перевірка | Використовується як доказ типу |
Приклад: як TS "думає"
if (isUser(obj)) {
// TypeScript "повірив" перевірці isUser
// obj: User
} else {
// obj: unknown
}TypeScript не виконує функцію,
він просто довіряє анотації value is Type -
це обіцянка розробника, що логіка функції справді правильна.
Важливі зауваження
- TypeScript не перевіряє коректність перевірки всередині - це відповідальність розробника.
function isNumber(x: any): x is number {
return typeof x === "string"; // TS повірить, навіть якщо це помилка
}- Type guards працюють лише з параметром функції, а не з довільними змінними.
- Можна використовувати generics, щоб робити універсальні guards.
Підсумок
| Термін | Визначення |
|---|---|
| Type Guard Function | Функція, яка повертає value is Type і повідомляє TS, що значення має вказаний тип |
| Призначення | Звуження типів і безпечна робота з unknown, any та union-типами |
| Головна особливість | Впливає на аналіз типів, а не лише на логіку виконання |
| Типовий синтаксис | function isUser(obj: any): obj is User { ... } |
| Головна перевага | Безпека, автодоповнення і відсутність as |
Просто запам'ятай:
Type Guard Function - це "охоронець", який каже TypeScript: "я перевірив, можеш довіряти - це саме цей тип".
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.