Веб-розробка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 вер.
Читати далі