← Назад к вопросам

Для чего нужны guards?

2.0 Middle🔥 201 комментариев
#Архитектура и паттерны#Фреймворки и библиотеки

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Guards в TypeScript

Guards (охранники типов) — это механизм, который помогает TypeScript и разработчикам определить точный тип значения во время выполнения программы. Они критически важны для работы с динамическими данными, особенно в backend-приложениях, где данные приходят из внешних источников.

Основная проблема

В runtime TypeScript типы исчезают, остаётся обычный JavaScript:

function processData(data: unknown) {
  // TypeScript не знает, что здесь число!
  // Может быть строка, объект, null, всё что угодно
  console.log(data.toFixed(2)); // Ошибка!
}

Guards решают эту проблему.

Type Guards

1. typeof guard — проверка примитивных типов

function printLength(value: unknown) {
  if (typeof value === 'string') {
    // Теперь TypeScript знает, что это строка
    console.log(value.length); // ОК
  } else if (typeof value === 'number') {
    console.log(value.toFixed(2)); // ОК
  }
}

2. instanceof guard — проверка объектов и классов

class User {
  constructor(public name: string) {}
}

function handleObject(obj: unknown) {
  if (obj instanceof User) {
    console.log(obj.name); // ОК, TypeScript знает тип
  }
  if (obj instanceof Date) {
    console.log(obj.getFullYear()); // ОК
  }
}

3. Пользовательские type guards (type predicate)

Самые мощные guards — это функции с типом is:

interface User {
  id: number;
  name: string;
  email: string;
}

// Это guard функция
function isUser(obj: unknown): obj is User {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    'id' in obj &&
    'name' in obj &&
    'email' in obj &&
    typeof (obj as any).id === 'number' &&
    typeof (obj as any).name === 'string' &&
    typeof (obj as any).email === 'string'
  );
}

const data = JSON.parse(someString);
if (isUser(data)) {
  // Теперь TypeScript уверен, что это User
  console.log(data.email);
} else {
  console.error('Invalid user data');
}

4. in guard — проверка свойств объекта

function processPerson(obj: unknown) {
  if (typeof obj === 'object' && obj !== null) {
    if ('isAdmin' in obj) {
      // Это администратор
      console.log('Admin detected');
    }
  }
}

Guards в Backend контексте

Валидация API запросов

interface CreateUserDTO {
  name: string;
  email: string;
  age: number;
}

function isCreateUserDTO(data: unknown): data is CreateUserDTO {
  return (
    typeof data === 'object' &&
    data !== null &&
    typeof (data as any).name === 'string' &&
    typeof (data as any).email === 'string' &&
    typeof (data as any).age === 'number' &&
    (data as any).age > 0
  );
}

app.post('/users', (req, res) => {
  const data = req.body;
  if (!isCreateUserDTO(data)) {
    return res.status(400).json({ error: 'Invalid data' });
  }
  // Теперь мы уверены в типе data
  createUser(data); // data типизирован как CreateUserDTO
});

Работа с Union типами

type Response = { status: 'success'; data: any } | { status: 'error'; message: string };

function isSuccessResponse(res: Response): res is { status: 'success'; data: any } {
  return res.status === 'success';
}

const response = fetchData();
if (isSuccessResponse(response)) {
  console.log(response.data);
} else {
  console.error(response.message);
}

Работа с внешними API

interface ExternalAPIResponse {
  userId: number;
  userName: string;
}

function isExternalAPIResponse(obj: unknown): obj is ExternalAPIResponse {
  return (
    typeof obj === 'object' &&
    obj !== null &&
    typeof (obj as any).userId === 'number' &&
    typeof (obj as any).userName === 'string'
  );
}

async function fetchUserFromAPI(id: string) {
  const response = await fetch(`https://api.example.com/user/${id}`);
  const data = await response.json();
  
  if (isExternalAPIResponse(data)) {
    return data; // Безопасно!
  } else {
    throw new Error('Unexpected API response format');
  }
}

Лучшие практики

Используй утилиты для валидации

Для сложных guards лучше использовать библиотеки:

// zod для валидации
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email()
});

type User = z.infer<typeof UserSchema>;

const data = JSON.parse(someString);
const result = UserSchema.safeParse(data);
if (result.success) {
  // result.data типизирован как User
  console.log(result.data);
}

Почему guards важны

  • Runtime безопасность: защита от неожиданных типов данных
  • Раннее обнаружение ошибок: перехватываем проблемы до того, как они пойдут дальше
  • Самодокументирующийся код: ясно видно, какие типы ожидаются
  • Developer experience: IDE подсказывает корректные методы после guard

Guards — неотъемлемая часть надёжного TypeScript backend кода.

Для чего нужны guards? | PrepBro