Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Type Guard в TypeScript: что это, как использовать, примеры
Что такое Type Guard
Type Guard — это функция или выражение в TypeScript, которое гарантирует для компилятора, что переменная имеет определённый тип в определённом месте кода.
Зачем это нужно:
- TypeScript не может автоматически определить тип переменной в runtime
- Type Guard помогает компилятору понять, какой тип у переменной
- Это даёт нам type safety и автодополнение в IDE
Простейший Type Guard: typeof
// Функция, которая может принять число или строку
function processValue(value: number | string) {
// Без type guard - TypeScript не знает, какой метод доступен
// value.toUpperCase(); // Ошибка: число не имеет toUpperCase()
// С type guard
if (typeof value === 'string') {
console.log(value.toUpperCase()); // ✅ TypeScript знает, что это string
} else {
console.log(value.toFixed(2)); // ✅ TypeScript знает, что это number
}
}
Type Guard с instanceof
Для проверки типа объектов/классов:
// Определяем классы
class User {
constructor(public name: string) {}
}
class Admin extends User {
constructor(name: string, public permissions: string[]) {
super(name);
}
}
// Type Guard используя instanceof
function processUser(user: User | Admin) {
if (user instanceof Admin) {
// Здесь TypeScript знает, что это Admin
console.log(`Admin ${user.name} has permissions:`, user.permissions);
} else {
// Здесь TypeScript знает, что это User
console.log(`User ${user.name}`);
}
}
Custom Type Guard: функция с type predicate
Это самый мощный способ создавать Type Guard'ы:
// Интерфейсы
interface Dog {
type: 'dog';
bark(): void;
}
interface Cat {
type: 'cat';
meow(): void;
}
// Type Guard функция
function isDog(animal: Dog | Cat): animal is Dog {
return animal.type === 'dog';
}
function isCat(animal: Dog | Cat): animal is Cat {
return animal.type === 'cat';
}
// Использование
const pet: Dog | Cat = { type: 'dog', bark: () => console.log('Woof!') };
if (isDog(pet)) {
pet.bark(); // ✅ TypeScript знает, что это Dog
} else if (isCat(pet)) {
pet.meow(); // ✅ TypeScript знает, что это Cat
}
Type Guard в реальных сценариях
Сценарий 1: Обработка API response'ов
// Ответы от API могут быть разных типов
interface SuccessResponse {
status: 'success';
data: unknown;
}
interface ErrorResponse {
status: 'error';
error: string;
}
type APIResponse = SuccessResponse | ErrorResponse;
// Type Guard
function isSuccess(response: APIResponse): response is SuccessResponse {
return response.status === 'success';
}
// Использование
async function fetchData(): Promise<APIResponse> {
const response = await fetch('/api/data');
return response.json();
}
async function handleData() {
const response = await fetchData();
if (isSuccess(response)) {
console.log('Data:', response.data); // ✅ TypeScript знает структуру
} else {
console.error('Error:', response.error); // ✅ TypeScript знает структуру
}
}
Сценарий 2: Обработка разных типов ошибок
class ValidationError extends Error {
constructor(public field: string, public message: string) {
super(`${field}: ${message}`);
}
}
class DatabaseError extends Error {
constructor(public code: string) {
super(`Database error: ${code}`);
}
}
// Type Guard
function isValidationError(error: Error): error is ValidationError {
return error instanceof ValidationError;
}
function isDatabaseError(error: Error): error is DatabaseError {
return error instanceof DatabaseError;
}
// Использование
try {
// code
} catch (error) {
if (error instanceof Error) {
if (isValidationError(error)) {
console.log(`Validation failed on field: ${error.field}`);
} else if (isDatabaseError(error)) {
console.log(`Database error: ${error.code}`);
} else {
console.log(`Unknown error: ${error.message}`);
}
}
}
Сценарий 3: Nullish coalescing (проверка на null/undefined)
// Функция может вернуть пользователя или null
function getUser(id: string): User | null {
// ...
}
// Type Guard для проверки на null
function isUser(value: User | null): value is User {
return value !== null && value !== undefined;
}
// Использование
const user = getUser('123');
if (isUser(user)) {
console.log(user.name); // ✅ TypeScript знает, что это не null
} else {
console.log('User not found');
}
// Или более современный способ
if (user) {
console.log(user.name);
}
Type Guard для Discriminated Union
Это очень полезная техника:
// Discriminated Union - заказ может быть в разных статусах
type OrderState =
| { status: 'pending'; estimatedTime: Date }
| { status: 'shipped'; trackingNumber: string }
| { status: 'delivered'; deliveryDate: Date }
| { status: 'cancelled'; reason: string };
// Type Guard'ы
function isPending(order: OrderState): order is OrderState & { status: 'pending' } {
return order.status === 'pending';
}
function isShipped(order: OrderState): order is OrderState & { status: 'shipped' } {
return order.status === 'shipped';
}
function isDelivered(order: OrderState): order is OrderState & { status: 'delivered' } {
return order.status === 'delivered';
}
function isCancelled(order: OrderState): order is OrderState & { status: 'cancelled' } {
return order.status === 'cancelled';
}
// Использование
function handleOrder(order: OrderState) {
if (isPending(order)) {
console.log(`Order will be ready in ${order.estimatedTime}`);
} else if (isShipped(order)) {
console.log(`Track your package: ${order.trackingNumber}`);
} else if (isDelivered(order)) {
console.log(`Delivered on ${order.deliveryDate}`);
} else if (isCancelled(order)) {
console.log(`Order cancelled: ${order.reason}`);
}
}
Type Guard vs Type Assertion
Важное различие:
// ❌ Type Assertion - просто говорим компилятору, верить нам
const value: any = 'hello';
const length = (value as string).length; // 5
// Но если value на самом деле число - ошибка в runtime!
const value2: any = 123;
const length2 = (value2 as string).length; // undefined
// ✅ Type Guard - проверяем тип в runtime
function getValue(value: any) {
if (typeof value === 'string') {
console.log(value.length); // 5
} else if (typeof value === 'number') {
console.log('Not a string'); // Not a string
}
}
Type Guard в функциях фильтрации
// Массив с разными типами
const values: (string | number | null)[] = ['hello', 42, null, 'world', 123];
// Type Guard для фильтрации
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// Использование с filter
const strings = values.filter(isString);
// Тип: string[]
// Значение: ['hello', 'world']
// Или в одну строку
const numbers = values.filter((v): v is number => typeof v === 'number');
// Тип: number[]
// Значение: [42, 123]
Практическое применение в сервисах
// services/PaymentService.ts
type PaymentMethod =
| { type: 'credit_card'; cardNumber: string; cvv: string }
| { type: 'paypal'; email: string }
| { type: 'crypto'; address: string };
// Type Guard'ы
function isCreditCard(payment: PaymentMethod): payment is PaymentMethod & { type: 'credit_card' } {
return payment.type === 'credit_card';
}
function isPayPal(payment: PaymentMethod): payment is PaymentMethod & { type: 'paypal' } {
return payment.type === 'paypal';
}
function isCrypto(payment: PaymentMethod): payment is PaymentMethod & { type: 'crypto' } {
return payment.type === 'crypto';
}
// Сервис
class PaymentService {
async processPayment(amount: number, method: PaymentMethod) {
if (isCreditCard(method)) {
return this.processCreditCard(amount, method.cardNumber, method.cvv);
} else if (isPayPal(method)) {
return this.processPayPal(amount, method.email);
} else if (isCrypto(method)) {
return this.processCrypto(amount, method.address);
}
}
private processCreditCard(amount: number, cardNumber: string, cvv: string) {
// Логика для кредитной карты
}
private processPayPal(amount: number, email: string) {
// Логика для PayPal
}
private processCrypto(amount: number, address: string) {
// Логика для крипто
}
}
Встроенные Type Guard'ы TypeScript
// Проверка на массив
function isArray(value: unknown): value is unknown[] {
return Array.isArray(value);
}
// Проверка на объект
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
// Проверка на функцию
function isFunction(value: unknown): value is Function {
return typeof value === 'function';
}
// Использование
const data = { name: 'John' };
if (isObject(data)) {
console.log(data.name); // ✅ Доступно
}
Лучшие практики Type Guard'ов
-
Всегда возвращай правильный тип в predicate
// ✅ Хорошо function isUser(value: unknown): value is User { return typeof value === 'object' && value !== null && 'id' in value; } -
Используй для дискриминирующих объединений
// ✅ Идеально для Discriminated Union type Result = { success: true; data: string } | { success: false; error: Error }; -
Не используй Type Assertion вместо Type Guard
// ❌ Опасно const value = getData() as User; // ✅ Безопасно if (isUser(value)) { // использовать value как User } -
Кэшируй результаты Type Guard'ов
const isPayment = isValidPayment(data); if (isPayment) { processPayment(data); // data правильного типа }
Выводы
Type Guard — это:
- Способ помочь TypeScript понять типы в runtime
- Используется для проверки union types
- Даёт type safety и лучше автодополнение
- Может быть простым (typeof, instanceof) или сложным (custom функции)
- Особенно полезен для Discriminated Union паттернов
- Предпочтительнее Type Assertion'ов
Типичное использование:
type Data = A | B | C;
if (isA(data)) {
// работаем с A
} else if (isB(data)) {
// работаем с B
} else {
// работаем с C
}