Веб-разработкаE-commerce и бизнес

154 ошибки типов, которые выбрасывала сборка: что в них нашлось

21 сентября 2026 года проверка типов на нашем сайте показала 154 ошибки, хотя сборка каждый раз была зелёной: результат проверки просто выбрасывался. Внутри нашлись нули в статистике ссылок и в экспорте аналитики, сортировка, которая не сортировала, и тесты, которые не запускались. Рассказываем, как разбирали, что изменили и как проверить свой проект.

22 сентября 2026 г.
7 мин чтения

21 сентября 2026 года мы запустили на собственном сайте npx tsc --noEmit. Эта проверка ничего не собирает, а только проверяет типы в TypeScript-проекте. Результат: 154 ошибки.

Странно здесь не число. Странно, что ни одна из этих ошибок ни разу не остановила сборку. В tsconfig.json стояло strict: true, самый строгий режим проверки. А в next.config.mjs рядом жили две строки:

typescript: { ignoreBuildErrors: true },
eslint: { ignoreDuringBuilds: true },

Проверка выполнялась при каждой сборке, и её результат каждый раз выбрасывался. Сборка оставалась зелёной при любом количестве ошибок. У линтера в тот же день было 857 замечаний; крупнейшие группы — 547 о типе any и 300 о неиспользуемых переменных.

Ниже о том, что скрывалось внутри этих 154 ошибок, почему такие дефекты никому не мешают и как проверить, защищает ли вас ваша сборка.

Сначала решить, что считать

154 — это всё вместе. 65 ошибок были в разовых скриптах и тестах, которые в прод не попадают. Остальные 89 — в коде, который обслуживает посетителей и админку.

Поэтому первым шагом мы вынесли скрипты в отдельный tsconfig. Ошибку в скрипте, который запускали один раз для переноса данных, стоит исправить, но о состоянии сайта она ничего не говорит. К моменту этого разделения ошибок оставалось 132, и 59 из них сидели именно в скриптах. Считать нужно то, что работает в проде, иначе приоритеты размываются с первого дня.

Два дефекта, которые показывали нули

Самыми дорогими оказались не те ошибки, которые могли бы уронить страницу. Страницы работали. Проблемы сидели там, где сайт показывает цифры.

Статистика коротких ссылок в админке. Для каждой ссылки там есть карточки и графики: с каких устройств переходили, из каких стран. Все они показывали ноль. Сервер отдавал разбивку словарём, примерно так:

{ desktop: 206, mobile: 33 }

Интерфейс же ждал массив и проверял вход через Array.isArray. Словарь эту проверку не проходит, поэтому компонент решал, что данных нет, и рисовал ноль. TypeScript говорил об этом прямо: объект нельзя присвоить типу массива. Сборка это сообщение выбрасывала.

После исправления на нашей ссылке с 252 кликами появилось то, что там было всё время: 206 переходов с десктопа, 33 с мобильных, топ-страна Украина со 199 кликами.

Экспорт аналитики. Кнопки выгрузки в CSV, Excel и PDF работали, файлы скачивались, а в каждой ячейке стоял ноль. Данные хранились как словарь «период → число», а код экспорта искал в нём объекты с полем .count. У числа такого поля нет, так что каждое значение превращалось в ноль. Итоговая строка выглядела ещё красноречивее: 0[object Object]. Сумму считали, прибавляя к нулю объекты вместо чисел, и JavaScript склеил их в строку.

Для владельца сайта последствие в обоих случаях одинаковое: отчёт есть, выглядит нормально и врёт. Решение, принятое по такому отчёту, принято по нулям.

Остальные живые дефекты

Во вкладке SEO-страниц админки сортировка по колонке молча игнорировалась. Интерфейс отправлял параметры sortBy и sortOrder, а сервер ждал orderBy и orderDir. Стрелка в заголовке переключалась, список оставался в том же порядке. Человек, который на это смотрит, скорее решит, что так задумано, чем пойдёт искать расхождение в названиях параметров.

Аналитика кнопки «поделиться» писалась со сдвигом: в метку платформы попадал адрес статьи, а в адрес статьи — слово «button». Аргументы функции стояли не в том порядке.

Ещё одна мелочь касалась письма-подтверждения из контактной формы. Шаблон ждал поле «компания», которого нет ни в форме, ни в базе, поэтому строка «от компании …» в письме не появлялась никогда.

Учёт запросов, заработавший 20 сентября, имел утечку меток. Для маршрутов под /api метка бралась из сырого адреса, и боты, перебирающие адреса вроде /api/.env или /api/credentials.yml, за первые сутки дали 15 из 30 меток API. Каждый новый адрес сканера — новая серия в системе метрик навсегда. Теперь все 404 под /api сливаются в одну метку.

Самой странной находкой был модуль CDN. CDN у сайта нет, а эндпоинт без какой-либо авторизации отдавал кому угодно выдуманную статистику: «доля попаданий в кеш 81,6%» с январскими датами. Функция «очистки кеша» ничего не очищала, а лишь писала в лог Cache purged successfully. Мы удалили модуль вместе с другим кодом, который никто не вызывал, — всего около 1 300 строк.

Ловушки, которые ещё не сработали

Некоторые находки ничего не ломали только потому, что соответствующий код сейчас не выполняется.

В проекте жили две одноимённые функции учёта запросов с разным порядком аргументов. Один модуль импортировал не ту, и код статуса 200 оказывался в метке HTTP-метода. Пока этот путь не вызывается, вреда нет. Как только его подключили бы, метрики были бы перемешаны с первого же запроса.

Тесты тоже оказались декорацией: vitest не запускался вообще, потому что не хватало пакета окружения jsdom. Тестовый файл маршрута проверки состояния описывал его версию, которой давно не существовало. После исправления в проекте 67 тестов, все проходят.

Остальные ошибки относились к типу «тип врёт о рантайме». Поле есть в базе, но его нет в ручном описании типа; один и тот же тип объявлен дважды; у параметра функции вообще нет типа. Работу сайта это не ломало. Однако живые дефекты выше сидели именно в этом шуме, и добраться до них можно было, только пройдя его весь.

Почему это молчит

Зелёная сборка — самый сильный сигнал «всё в порядке», который есть в разработке. Когда ошибки игнорируются, их никто не видит, и каждая новая тонет среди старых.

Флаг ignoreBuildErrors часто появляется по вполне понятной причине: правку нужно выложить срочно, а сборка падает на чём-то постороннем. Его включают один раз, чтобы быстро задеплоить, и забывают.

Важнее другое: эти дефекты не роняли страницы. Админка показывала нули, а не ошибку. Ноль — правдоподобное значение, ведь переходов действительно могло не быть. О тихих нулях никто не сообщает. Похожий узор мы уже разбирали в истории про мониторинг, который жил только в документации: метрики объявлены, панели стоят, а данных под ними нет.

Как мы разбирали

Каждую ошибку читали как вопрос «что здесь происходит во время выполнения?», а не «как это заглушить». as any или // @ts-ignore убирают красное подчёркивание за секунду и прячут дефект надолго.

Больше всего внимания получили такие сообщения:

  • «свойство не существует» (TS2339): код читает поле, которого в данных нет, а значит, всегда получает undefined;
  • «аргумент не того типа» (TS2345): часто за этим стоит перепутанный порядок аргументов, как в кнопке «поделиться»;
  • «нельзя присвоить» (TS2322): данные имеют другую форму, чем ждёт интерфейс. Именно так выглядели нули в статистике ссылок.

Каждый найденный дефект проверяли отдельно, данными из базы или запуском. Исчезновение ошибки в редакторе доказательством не считали: его можно получить и неправильным исправлением.

Что изменилось 22 сентября

Когда ошибок типов в коде стало ноль, мы отключили игнорирование: ignoreBuildErrors: false. Теперь новая ошибка типов останавливает сборку и до прода не доходит.

Линтер в сборке пока отключён. Там ещё сотни замечаний про any, и остановка на нём сейчас означала бы остановку каждого деплоя. Неиспользуемые переменные убрали механически: было 291, стало 106.

Порядок шагов здесь имеет значение. Если бы мы сначала отключили игнорирование, а потом начали исправлять, первый же деплой остановился бы.

Как проверить у себя

Первое — учитывает ли сборка типы вообще:

grep -n "ignoreBuildErrors\|ignoreDuringBuilds" next.config.*

Если там true, зелёная сборка ничего не говорит о состоянии кода. Дальше посчитайте реальное количество ошибок:

npx tsc --noEmit | grep -c "error TS"

Отфильтруйте скрипты и тесты и смотрите на код приложения: он и есть сайт. Начинать стоит с трёх кодов, которые чаще всего означают настоящее расхождение между данными и кодом:

npx tsc --noEmit | grep -E "TS2339|TS2345|TS2322"

Отдельно проверьте, стартуют ли тесты: npx vitest run или npm test. Тесты, которые не запускаются, ничего не проверяют, сколько бы их ни было в репозитории.

Остановку сборки включайте только после того, как ошибок станет ноль. Иначе следующий деплой остановится, и флаг вернут обратно.

Если разработчика под рукой нет, попросите кого-нибудь выполнить две первые проверки и показать результат. Когда в конфигурации true, а ошибок десятки, цифрам в админке стоит устроить выборочную проверку: откройте статистику, правильный ответ для которой вы знаете из другого источника, и сверьте.

Границы этого разбора

Это один проект — наш сайт. Из 154 ошибок у нас нашлось семь живых дефектов и несколько ловушек, которые ещё не успели сработать. В другом проекте под теми же ошибками может оказаться больше, меньше или ничего. Количество ошибок типов не говорит, сколько под ними поломок. Оно говорит лишь о том, что сборка о них молчит.

Если проект достался вам от другого исполнителя и неизвестно, что в нём работает, а что только так выглядит, такой разбор входит в подхват заброшенного проекта. Когда конкретные поломки уже замечены, это исправление после подрядчика.

Для себя мы вынесли одно правило: проверка, результат которой выбрасывается, не лучше отсутствия проверки. Она лишь создаёт впечатление, что защита есть.

Теги

Performance

🤔Вам понравилась статья?

Ваше мнение помогает нам создавать лучший контент

Поделитесь с друзьями

Нашли что-нибудь полезное? 🚀

Помогите другим узнать это — поделитесь статьей в социальных сетях

https://lionex.com.ua/blog/pomylky-typiv-yaki-vykydala-zbirka

💚 Спасибо, что помогаете нам расти

Владислав Чистяков

Пишет о том, что делает руками: интернет-магазины на OpenCart, приложения на Next.js, интеграции и скорость сайтов. В статьях — замеры и проверки, которые читатель может повторить на своём проекте, а не общие советы. Коммерческая разработка с 2015 года.

Частые вопросы

Ответы на популярные вопросы по теме

Next.js во время сборки проверяет типы, но с этим флагом результат проверки игнорируется: сборка остаётся зелёной при любом количестве ошибок. У нас 21 сентября 2026 года так было при включённом strict: true, и проверка tsc --noEmit показала 154 ошибки, которых сборка не замечала.

Когда данные имеют другую форму, чем ждёт интерфейс. В нашей админке сервер отдавал разбивку кликов словарём, а карточки ждали массив и проверяли его через Array.isArray; словарь проверку не проходил, и показывался ноль. TypeScript об этом предупреждал, но сборка сообщение выбрасывала. После исправления на ссылке с 252 кликами появились 206 переходов с десктопа и 33 с мобильных.

Потому что они не роняют страницы. Админка показывает правдоподобный ноль, а не ошибку, и о таком нуле никто не сообщает. Зелёная сборка при этом создаёт впечатление, что всё в порядке, а каждая новая ошибка типов тонет среди старых.

Найдите в next.config строки ignoreBuildErrors и ignoreDuringBuilds и посчитайте ошибки через npx tsc --noEmit | grep -c "error TS". Отделите разовые скрипты и тесты от кода приложения. Первыми читайте ошибки TS2339, TS2345 и TS2322: они чаще всего означают настоящее расхождение между данными и кодом. Отдельно проверьте, запускаются ли тесты вообще.

Можно, но тогда первый же деплой остановится на накопившихся ошибках. Мы сначала довели количество ошибок типов в коде приложения до нуля и только потом, 22 сентября 2026 года, включили остановку сборки. Линтер в сборке пока остался отключённым, потому что там ещё сотни замечаний про any.

Получайте лучшие статьи на почту

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

Мы уважаем вашу конфиденциальность. Отписаться можно в любой момент.

Похожие статьи

Все статьи
Сертификаты продлеваются сами: TLS на 116 сайтах и один, который не продлился
Веб-разработка

Сертификаты продлеваются сами: TLS на 116 сайтах и один, который не продлился

22 сентября 2026 года мы проверили HTTPS-сертификаты на 116 сайтах, которые уже измеряли для рыночных исследований, и для 104 из них сравнили состояние с августовским. Все 116 прошли проверку, 108 стоят на 89- и 90-дневных бесплатных сертификатах, и только у одного сайта сертификат до сих пор не продлился, хотя при типичных настройках уже должен был. Разбираем замеры, границы метода и показываем, как проверить свой сайт одной командой.

9 мин
22 сент.
Читать дальше
Healthcheck дал 60 % запросов приложения: что показали первые сутки метрик
Веб-разработка

Healthcheck дал 60 % запросов приложения: что показали первые сутки метрик

21 сентября 2026 года, в первые сутки учёта запросов по маршрутам, служебный /api/health получил 13 666 запросов — 60 % трафика приложения, и каждый шёл в общую базу. Рассказываем, какие две настройки это убрали, какую цену мы за них платим и как проверить свой healthcheck.

9 мин
22 сент.
Читать дальше
Мы потеряли собственный магазин вместе с доменом: что об этом знает веб-архив
Техническое SEO

Мы потеряли собственный магазин вместе с доменом: что об этом знает веб-архив

17 сентября 2026 мы пересчитали по веб-архиву, как выглядела потеря собственного магазина вместе с доменом: 2 378 снимков главной в марте 2022, первый 301 первого апреля, последний снимок нашего содержимого 18 мая. Показываем, что из архива восстанавливается, а что нет.

9 мин
17 сент.
Читать дальше