Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Middle: питання на співбесіді з теми «Типи й приведення»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

4 питання

Усі числа в JavaScript (крім BigInt) - 64-бітні числа з плаваючою комою за стандартом IEEE 754. Більшість десяткових дробів, як 0.1, у двійковій системі - нескінченні дроби, тож зберігаються з крихітною похибкою.

0.1 + 0.2;           // 0.30000000000000004
0.1 + 0.2 === 0.3;   // false
1.005 * 100;         // 100.49999999999999

Це не баг JavaScript - так само поводяться float у PHP, Python, Java.

Як порівнювати дроби: з допуском:

Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true

Як рахувати гроші:

  • У найменших одиницях - копійках, як цілі числа: 1999 замість 19.99. Цілі числа до Number.MAX_SAFE_INTEGER (≈ 9 квадрильйонів) представлені точно.
  • Округлення - явне й в одному місці: Math.round(price * quantity).
  • Для складних розрахунків (відсотки, курси, розподіл) - бібліотеки десяткової арифметики (decimal.js, big.js, dinero.js).
  • Показ користувачу - через Intl.NumberFormat:
new Intl.NumberFormat('uk-UA', { style: 'currency', currency: 'UAH' }).format(1999 / 100);
// '19,99 ₴'

toFixed() - для показу, не для розрахунків: він повертає рядок і сам округлює з тією ж похибкою ((1.005).toFixed(2) дає '1.00').

Докладніше в документації: Number

У логічному контексті (if, &&, ||, !, тернарний оператор) кожне значення перетворюється на true чи false. Хибних значень небагато:

false, 0, -0, 0n, '' (порожній рядок), null, undefined, NaN.

Усе інше - істинне, і саме тут сюрпризи:

Boolean('0');      // true - непорожній рядок
Boolean('false');  // true
Boolean([]);       // true - порожній масив
Boolean({});       // true - порожній об'єкт

Де це підводить:

  • Нуль як валідне значення:
function setVolume(level) {
  if (!level) level = 50;   // гучність 0 перетвориться на 50
}

Правильно: level ??= 50 чи явна перевірка level === undefined.

  • Перевірка порожнього масиву: if (items) завжди істинне. Потрібно if (items.length > 0).
  • Рендер у React: {count && <Badge />} при count === 0 виведе на сторінку 0. Правильно: {count > 0 && <Badge />}.
  • Рядки з форми: значення з <input> завжди рядок, і '0' - істинний.

!!value чи Boolean(value) явно перетворюють значення на логічне - корисно, коли функція має повертати саме true/false.

Докладніше в документації: Falsy

NaN (Not a Number) - спеціальне значення типу number, яке означає результат невизначеної чи неможливої числової операції:

typeof NaN;          // 'number'
Number('abc');       // NaN
0 / 0;               // NaN
Math.sqrt(-1);       // NaN
undefined + 1;       // NaN

NaN не дорівнює нічому, навіть собі. Так визначено стандартом IEEE 754 для чисел з рухомою комою:

NaN === NaN;   // false
NaN == NaN;    // false
const x = NaN;
x !== x;       // true - єдине значення з такою властивістю

Тому if (value === NaN) ніколи не спрацює.

Як перевіряти правильно:

Number.isNaN(value);     // true лише для справжнього NaN
Object.is(value, NaN);   // теж true

Не плутати з глобальною isNaN(). Вона спершу перетворює аргумент на число, тож відповідає на інше питання - «чи стане це NaN після перетворення»:

isNaN('abc');          // true - 'abc' перетворився на NaN
Number.isNaN('abc');   // false - це рядок, а не NaN
isNaN(undefined);      // true
isNaN('');             // false - '' перетворюється на 0

Для перевірки «чи є це коректним числом» найзручніше Number.isFinite(value): false для NaN, Infinity і будь-яких нечислових типів.

NaN «заражає» обчислення: будь-яка арифметика з NaN дає NaN. Одне некоректне значення в масиві перетворить суму на NaN:

[10, 20, Number('')].reduce((a, b) => a + b);       // 30 ('' дає 0)
[10, 20, Number('x')].reduce((a, b) => a + b);      // NaN

Звідси правило: перевіряти дані на вході (при парсингу форми чи відповіді API), а не шукати, де з'явився NaN, у кінці розрахунку.

Різні методи по-різному бачать NaN:

  • [NaN].includes(NaN) → true (алгоритм SameValueZero);
  • [NaN].indexOf(NaN) → -1 (строга рівність);
  • new Set([NaN, NaN]).size → 1;
  • JSON.stringify(NaN) → 'null' - NaN тихо перетворюється на null при відправці на сервер.

Докладніше в документації: Number.isNaN()

Об'єкт Date зберігає одну мить у часі - кількість мілісекунд від 1970-01-01 UTC. Часового поясу в ньому немає: методи на кшталт getHours() показують цю мить у поясі браузера користувача, а getUTCHours() - в UTC.

Головні пастки:

1. Місяці з нуля.

new Date(2026, 0, 31);   // 31 січня 2026
new Date(2026, 1, 31);   // 3 березня - «переповнення» тихо переноситься

2. Розбір рядків залежить від формату.

new Date('2026-10-04');        // північ за UTC (лише дата - як UTC)
new Date('2026-10-04T10:00');  // 10:00 за місцевим часом браузера
new Date('04.10.2026');        // залежить від рушія - не використовувати

Користувач у Києві побачить new Date('2026-10-04').getDate() як 4, а в Нью-Йорку - як 3: північ UTC там ще попередній день.

3. Об'єкт змінюваний.

const start = new Date();
const end = start;
end.setDate(end.getDate() + 7);   // змінили і start теж

4. Арифметика місяців. setMonth(month + 1) від 31 січня дає 3 березня (лютого 31-го не існує).

Як працювати безпечно:

  • обмінюватися з сервером у ISO 8601 з поясом: 2026-10-04T07:00:00Z чи з зміщенням +03:00. Laravel за замовчуванням серіалізує дати моделей саме так (UTC з Z);
  • зберігати й передавати мить у UTC, а в місцевий час перетворювати лише для показу;
  • показувати через Intl.DateTimeFormat, а не збирати рядок вручну:
new Intl.DateTimeFormat('uk', {
  dateStyle: 'long',
  timeStyle: 'short',
  timeZone: 'Europe/Kyiv',
}).format(new Date('2026-10-04T07:00:00Z'));
// '4 жовтня 2026 р. о 10:00'
  • «дата без часу» (день народження, дата події) - не перетворювати на Date взагалі, а тримати рядком '2026-10-04', інакше часовий пояс зсуне її на день;
  • відносний час - Intl.RelativeTimeFormat('uk') («3 дні тому»).

Що далі. Новий API Temporal розділяє поняття: Temporal.PlainDate (дата без часу й поясу), Temporal.ZonedDateTime (час у конкретному поясі), Temporal.Instant (мить) - і робить їх незмінними. Він поступово з'являється в браузерах; до повної підтримки - поліфіл або бібліотеки (date-fns, Day.js, Luxon).

Докладніше в документації: Date