Оператор typeof у JavaScript
typeof повертає рядок з типом операнда. Оператор виконується під час роботи програми і може дати одне з 8 можливих значень.
Теорія
TL;DR
typeofсхожий на сканер ярликів на складі: примітиви отримують унікальні теги ("number", "string"), але всі об'єкти отримують однаковий тег "object", незалежно від вмісту.- Головне обмеження:
{},[]іnullповертають"object". Різниці між ними немає. - Правило вибору:
typeofдля примітивів;Array.isArray(),=== nullабоinstanceofдля об'єктів.
Швидкий приклад
typeof 42; // "number"
typeof "hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol("id"); // "symbol"
typeof 42n; // "bigint"
typeof function(){}; // "function"
typeof {}; // "object"
typeof []; // "object" -- те саме, що й {}
typeof null; // "object" -- баг з 1995 року, так і не виправлений
typeof NaN; // "number" -- NaN залишається числовим типомТри результати дивують розробників найчастіше: масиви, null і NaN.
Ключова різниця
typeof зчитує внутрішній тег значення, а не його структуру чи конструктор. Для кожного примітивного типу тег унікальний. Для всіх об'єктів (включно з масивами) і null тег однаковий: "object". Виняток складають функції: специфікація JS дає їм окремий тег, тому typeof function(){} повертає "function", а не "object".
Коли використовувати
- Захист типу:
if (typeof x === "string")перед викликом рядкових методів - Перевірка колбека:
typeof fn === "function"перед викликом переданої функції - Неоголошена змінна:
typeof undeclaredVarповертає"undefined"без ReferenceError - Не для об'єктів: використовуй
Array.isArray(arr)для масивів,value === nullдля null,instanceofдля екземплярів класів
Як це працює всередині
V8 реалізує typeof як одну байткод-операцію. Він зчитує тег з внутрішнього представлення значення в пам'яті: числа мають тег Smi, об'єкти вказують на HeapObject map. Перевірка займає менше наносекунди. null ділить тег об'єкта ще з часів Netscape 2.0 і ніколи не виправлявся, щоб не зламати існуючі сайти.
Поширені помилки
Помилка 1: Думати, що typeof x === "object" означає звичайний об'єкт.
typeof []; // "object"
typeof null; // "object"
// Виправлення: поєднай перевірки
if (x !== null && typeof x === "object" && !Array.isArray(x)) {
// тільки тепер x — справді звичайний об'єкт
}Помилка 2: Перевіряти NaN через typeof.
typeof NaN; // "number" -- NaN є числом за стандартом IEEE 754
// Виправлення:
Number.isNaN(NaN); // true
Number.isNaN("hello"); // false -- без приведення типівПомилка 3: Очікувати, що typeof розрізнить масив і об'єкт.
typeof []; // "object" -- не "array"
// Виправлення:
Array.isArray([]); // trueПомилка 4: Звертатися до неоголошеної змінної напряму.
console.log(undeclaredVar); // ReferenceError
console.log(typeof undeclaredVar); // "undefined" -- безпечно
// Практичне застосування: перевірка середовища у SSR / Next.js
if (typeof window !== "undefined") {
// код тільки для браузера
}Де зустрічається в реальному коді
- React:
if (typeof onClick !== "function") return null;для захисту від відсутнього пропа - Express.js:
if (typeof req.body.userId !== "string") return res.status(400)для валідації запитів - Node.js:
if (typeof callback === "function") fs.readFile(..., callback)у callback-based API - Lodash:
_.isString(val)всередині починається зtypeof val === "string"
Перевірка typeof window !== "undefined" є майже в кожному SSR-проєкті, з яким я працював. Це найпростіший спосіб відокремити браузерний код від серверного без помилок.
Питання на співбесіді
Q: Що повертає typeof null і чому?
A: Повертає "object". Це баг з 1995 року: перша реалізація JavaScript зберігала значення з числовими тегами, і null мав тег 0, який означав "object". Виправити це неможливо без ризику зламати величезну кількість існуючого коду.
Q: Як правильно перевірити, чи є значення масивом?
A: Використовуй Array.isArray(value). Він працює навіть між iframe-ами, на відміну від instanceof Array, який ламається, коли значення прийшло з іншого фрейму.
Q: У чому різниця між typeof і instanceof?
A: typeof повертає рядковий тег і працює з примітивами. instanceof перевіряє ланцюжок прототипів і працює лише з об'єктами. До того ж, instanceof ламається між iframe-ами, бо кожен фрейм має власний конструктор Array і Object.
Q: Чому typeof undeclaredVariable не кидає ReferenceError?
A: Це єдиний виняток в JavaScript, де неоголошений ідентифікатор не викликає помилки. Специфікація зробила такий виняток, щоб можна було безпечно перевіряти наявність опціональних глобалів.
Q: Чому typeof такий швидкий у V8?
A: Він зчитує один тег з внутрішнього представлення значення в пам'яті. Немає обходу прототипів, немає алокацій. Одна байткод-інструкція.
Приклади
Базовий: захист типу перед обробкою
function processInput(input) {
if (typeof input === "number") {
return input * 2;
}
if (typeof input === "string") {
return input.toUpperCase();
}
throw new Error("Unsupported type");
}
processInput(5); // 10
processInput("hi"); // "HI"
processInput([]); // Error: Unsupported typetypeof охороняє кожну гілку перед викликом методу. Масив потрапляє в помилку, бо typeof [] === "object", а не "array".
Середній: перевірка пропсів у React-компоненті
function UserCard({ user, onClick }) {
if (typeof user !== "object" || user === null) {
return <div>Некоректні дані користувача</div>;
}
if (typeof onClick !== "function") {
console.warn("onClick має бути функцією");
return null;
}
return <div onClick={onClick}>{user.name}</div>;
}
// Перевірка user === null обов'язкова:
// typeof null === "object", тому без неї null пройде першу умову.Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.