ГлавнаяБлогПочему TypeScript не понимает .filter(Boolean) и как это исправить
Алгоритмы

Почему TypeScript не понимает .filter(Boolean) и как это исправить

Разбираем, почему TypeScript оставляет undefined после .filter(Boolean), и учимся использовать type predicate для правильных типов. Читайте, чтобы писать безопасный код.

Al
Редакция Algolitalgolit.ru
6 мин чтения16 июня 2026 г.

Почему TypeScript не выводит тип после .filter(Boolean)?

Вчера при настройке CORS я получил от ИИ такой код:

const allowedOrigins = [
  process.env.FRONTEND_URL || "http://localhost:3000",
  process.env.ADMIN_URL || "http://localhost:3001",
].filter(Boolean);

Я задумался: зачем тут .filter(Boolean), если fallback уже гарантируют строки? Навёл на переменную — тип string[]. Всё логично. Но потом я убрал fallback:

const allowedOrigins = [
  process.env.FRONTEND_URL,
  process.env.ADMIN_URL,
].filter(Boolean);

Тип изменился на (string | undefined)[]. Я был в шоке. Как так? Ведь filter(Boolean) на runtime удаляет все falsy-значения, включая undefined. Почему TypeScript думает иначе?

Что делает .filter(Boolean)?

Boolean как функция-колбэк удаляет из массива любые falsy-значения: false, null, undefined, 0, "", NaN. Например:

["https://app.com", "", undefined].filter(Boolean)
// Result: ["https://app.com"]

На runtime всё работает идеально. Ни одного undefined не выживет. Так почему же TypeScript с этим не согласен?

Проблема: TypeScript не выполняет код

TypeScript — это транспилятор. Он не запускает .filter(Boolean), а только анализирует типы. Когда он видит:

array.filter(Boolean)

Он знает, что колбэк возвращает boolean. Но он не понимает, что это означает для типов выживших элементов. Он не может вывести: «если Boolean(x) истинно, то x — строка». Поэтому undefined остаётся в типе, хотя в рантайме его никогда не будет. Это разрыв: поведение корректно, но типы врут.

Решение: Type Predicate

TypeScript позволяет закрыть этот разрыв с помощью type predicate — явного указания компилятору, что гарантирует функция фильтрации:

const allowedOrigins = [
  process.env.FRONTEND_URL,
  process.env.ADMIN_URL,
].filter((origin): origin is string => Boolean(origin));
// Тип: string[] ✅

Часть origin is string — это предикат. Это обещание компилятору: «если функция вернула true, значение точно строка». TypeScript доверяет этому и сужает тип.

Утилита для переиспользования

Если такой паттерн встречается часто, вынесите его в хелпер:

function isDefined<T>(value: T | undefined | null): value is T {
  return value != null;
}

Тогда:

const allowedOrigins = [
  process.env.FRONTEND_URL,
  process.env.ADMIN_URL,
].filter(isDefined);
// Тип: string[] ✅

Переиспользуемо, самодокументируемо и красиво. Лично я предпочитаю такой вариант.

Почему с fallback всё работало?

Вернёмся к исходному коду с ||:

const allowedOrigins = [
  process.env.FRONTEND_URL || "http://localhost:3000",
  process.env.ADMIN_URL || "http://localhost:3001",
].filter(Boolean);

process.env.X || "fallback" всегда вычисляется в string. Fallback-строка покрывает случай undefined, поэтому TypeScript уже знает, что каждый элемент — строка, ещё до фильтрации. .filter(Boolean) здесь просто защитный манёвр — полезен, если кто-то позже добавит запись без fallback, но для корректности типов не нужен.

Шпаргалка

  • .filter(Boolean) — тип (string | undefined)[]. Используйте, когда тип результата не важен.
  • .filter((x): x is string => Boolean(x)) — тип string[]. Для однократного использования.
  • .filter(isDefined) — тип string[]. Для переиспользования в кодовой базе.
  • process.env.X || "fallback" — тип string. Когда нужен гарантированный запасной вариант.

Вывод

.filter(Boolean) — это рантайм-штука, которую TypeScript воспринимает как чёрный ящик. Если вам нужно, чтобы типы отражали реальное содержимое массива, используйте type predicate. Небольшое изменение — честные типы.

Спасибо за чтение! Теперь идите и примените isDefined в своём проекте.

#TypeScript#type predicate#filter#Boolean#типизация
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →