Типы данных в JavaScript и оператор typeof

Обходим ловушки типов данных

Типы данных в JavaScript и оператор typeof

Типы данных в JS на первый взгляд вроде бы простая штука, пока не начнёшь в неё погружаться. Здесь и сюрпризы с NaN, и странности с преобразованиями, и многое другое.

В этой статье разберёмся: какие типы есть в JavaScript, как они сравниваются, как правильно использовать оператор typeof и где вас точно подстерегают баги, если не быть внимательным.

Введение в типы данных

Если вы уже писали хоть пару строк на JavaScript, с типами данных уже знакомы. Числа, строки, булевы значения, объекты, массивы и так далее. Мы не будем расписывать каждый тип по пунктам, но ключевые моменты всё же напомним.

Типы делятся на примитивные и ссылочные.

Примитивы — это string, number, boolean, bigint, symbol, undefined и null. Они хранятся прямо в переменной, не имеют методов (но иногда ведут себя так, будто имеют) и при передаче в функцию копируются.

Ссылочные — это всё, что объект: Object, Array, Function, Date, RegExp и т. д. Они передаются по ссылке: передали массив в функцию, поменяли его внутри — снаружи он тоже поменялся. 

Важно помнить, что JavaScript — язык с динамической и нестрогой типизацией. То есть значение может быть числом, а потом строкой, а потом — NaN.

Полезный блок со скидкой

Если вам интересно разбираться со смартфонами, компьютерами и прочими гаджетами и вы хотите научиться создавать софт под них с нуля или тестировать то, что сделали другие, — держите промокод Практикума на любой платный курс: KOD (можно просто на него нажать). Он даст скидку при покупке и позволит сэкономить на обучении.

Бесплатные курсы в Практикуме тоже есть — по всем специальностям и направлениям, начать можно в любой момент, карту привязывать не нужно, если что.

Оператор typeof

В JavaScript типы данных могут вести себя непредсказуемо. Значение, которое было числом, в следующей строке уже становится строкой, а null внезапно оказывается объектом. Чтобы понять, с чем вы имеете дело прямо сейчас, используют оператор typeof. Это встроенный механизм, который возвращает строку с названием типа значения.

Он особенно полезен при работе с вводом пользователя, API, валидацией данных, а также в ситуациях, когда нужно убедиться, что переменная содержит именно то, что вы ожидаете.

Но typeof не всегда даёт интуитивные результаты — и в этом его особенность.

Синтаксис и использование

Оператор typeof — унарный. Это значит, он применяется к одному значению и возвращает строку с его типом. Использовать его можно как со скобками, так и без:

typeof значение
typeof(значение)

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

typeof 123            // 'number'
typeof 'JavaScript'   // 'string'
typeof true           // 'boolean'
typeof undefined      // 'undefined'
Типы данных в JavaScript и оператор typeof

Ещё можно использовать typeof в условиях, чтобы, например, проверить, пришло ли в функцию число:

function processValue(value) {
  if (typeof value === 'number') {
    // работаем с числом
  }
}

Удобно, когда заранее неизвестно, какой тип вы получите, особенно в динамических системах — при обработке форм, сетевых данных, пользовательского ввода.

Но typeof не проверяет содержимое или структуру, только тип значения. И работает он неидеально. В следующем разделе посмотрим, какие именно типы он может возвращать и где начинаются нюансы.

Типы данных, возвращаемые оператором

typeof всегда возвращает строку, обозначающую тип переданного значения. Их может быть восемь видов:

  1. Число: ‘number’
  2. Строка: ‘string’
  3. Булево значение: ‘boolean’
  4. Неопределённо: ‘undefined’
  5. Уникальный идентификатор: ‘symbol’
  6. Больше число: ‘bigint’
  7. Функция: ‘function’
  8. Объект: ‘object’

Отдельно стоит упомянуть null. Логика подсказывает, что это должен быть отдельный тип ‘null’, но:

typeof null // ‘object’

Типы данных в JavaScript и оператор typeof

Это историческая ошибка, которая была допущена ещё в ранних версиях JavaScript и теперь уже не подлежит исправлению — слишком много кода в интернете на неё полагается. Просто запоминаем: typeof null возвращает ‘object’, хотя null — это примитив.

А ещё массивы, даты, регулярные выражения и другие встроенные объекты — это тоже ‘object’:

typeof [1, 2, 3]          // 'object'
typeof new Date()        // 'object'
typeof /abc/             // 'object'
Типы данных в JavaScript и оператор typeof

typeof не делает различий между типами объектов, а просто говорит: «Это объект». Если нужно больше конкретики — например, понять, массив это или нет, — придётся использовать другие методы (например, Array.isArray()). Об этом мы поговорим позже.

Типы данных: примитивные и ссылочные

Мы уже говорили, что в JavaScript все типы данных делятся на две большие категории: примитивные и ссылочные. 

Примитивы — это «простые» значения. Они хранятся непосредственно в переменной и передаются по значению. Это значит, что при копировании создаётся новая копия значения, не связанная с оригиналом:

let a = 5;
let b = a;
b = 10;
console.log(a); // 5 — оригинал не изменился
Типы данных в JavaScript и оператор typeof

Ссылочные типы — это объекты. Такие значения не копируются, а передаются по ссылке. То есть, если мы присвоим один объект другому, оба будут указывать на одну и ту же область памяти:

let obj1 = { name: 'Alice' };
let obj2 = obj1;
obj2.name = 'Bob';
console.log(obj1.name); // 'Bob' — оба объекта ссылаются на одно и то же
Типы данных в JavaScript и оператор typeof

Это база всей сложности с мутацией данных, особенно в сложных структурах и при работе с состоянием в React, Vue и других фреймворках.

Оператор typeof отлично справляется с примитивами: возвращает ‘string’, ‘number’, ‘boolean’ и т. д. Но со ссылочными типами всё чуть сложнее. Там typeof возвращает ‘object’ практически для всего, что объект, и ‘function’ — для функций.

Преобразование типов

JavaScript слишком любит помогать разработчику, иногда даже тогда, когда его об этом не просили. Это проявляется в автоматическом приведении типов: строка может стать числом, undefined превратиться в NaN, а массив внезапно окажется true в условии.

Неявное преобразование

Неявное преобразование типов происходит тогда, когда JavaScript сам решает: «А давай я тут превращу строку в число, чтобы сравнение сработало». Иногда удобно, а иногда больше мешает, чем помогает.

'5' + 3        // '53' — строка, потому что + работает как конкатенация
Типы данных в JavaScript и оператор typeof
'5' - 3        // 2 — минус приводит строку к числу
Типы данных в JavaScript и оператор typeof
true + 1       // 2 — true становится 1
Типы данных в JavaScript и оператор typeof
false == 0     // true — приведение при нестрогом сравнении
Типы данных в JavaScript и оператор typeof
null == undefined // true — специальное правило языка
Типы данных в JavaScript и оператор typeof
[] == false    // true — и вот тут уже начинается магия
Типы данных в JavaScript и оператор typeof

JS приводит значения по разным схемам в зависимости от контекста:

  • При арифметических операциях (-, *, /) всё старается превратиться в числа.
  • При сложении с +, если хотя бы один операнд — строка, происходит конкатенация.
  • При сравнении с == — используется алгоритм абстрактного сравнения, где движок делает всё, чтобы сравнить хоть как-то.

Вроде логично, но чем больше таких «автоматических подарков» от движка, тем сложнее отлавливать ошибки. Особенно в ветках условий, где сравниваются значения разного типа.

Явное преобразование

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

Привести значение к строке можно через String():

String(123)      // '123'
String(true)     // 'true'
123 + ''         // '123' — работает, но это уже не совсем явное
Типы данных в JavaScript и оператор typeof

Иногда используют шаблонные строки:

`${value}`       // превращает всё во что угодно в строку

Для чисел используют Number() — он постарается превратить значение в число и, если не сможет, вернёт NaN.

Number('42')     // 42
Number('')       // 0 
Number('abc')    // NaN
Типы данных в JavaScript и оператор typeof

А ещё есть parseInt() и parseFloat() — они разбирают строку до первого нецифрового символа и могут работать с числами в формате ‘123px’.

parseInt('123px')    // 123
parseFloat('1.5em')  // 1.5
Типы данных в JavaScript и оператор typeof

Number(‘123px’) даст NaN, а parseInt() — 123. Так что выбирайте под задачу.

Для булевых значений используют Boolean():

Boolean(0)        // false
Boolean('hello')  // true
Boolean('')       // false
Типы данных в JavaScript и оператор typeof

Явное преобразование используют, когда:

  • хотят точно контролировать результат;
  • данные приходят из внешнего источника (например, из формы или из API);
  • важно поведение: Number(”) даст 0, а parseInt(”) — NaN.

Если есть сомнения, что лежит в переменной, сначала проверяйте через typeof, потом явно приводите тип.

Сравнение типов данных

В JavaScript значения можно сравнивать двумя способами: строго (===) и нестрого (==). Различие в одном знаке, но == включает в себя автоматическое приведение типов, а === нет.

Строгое и нестрогое равенство

Строгое равенство (===) сравнивает и значение, и тип:

42 === 42         // true
'42' === 42       // false
true === 1        // false
null === undefined // false
Типы данных в JavaScript и оператор typeof

Здесь всё логично, понятно и ожидаемо.

А вот нестрогое равенство (==) сначала приводит значения к одному типу, а уже потом сравнивает:

'42' == 42        // true — строка преобразована в число
Типы данных в JavaScript и оператор typeof
false == 0        // true
Типы данных в JavaScript и оператор typeof

Вот случай, когда == возвращает true между значениями разных типов без приведения к числу. null и undefined равны только между собой:

null == undefined // true
Типы данных в JavaScript и оператор typeof

Дальше ещё интереснее:

[] == false  // true
Типы данных в JavaScript и оператор typeof

Типы разные — один object ([]), другой boolean, значит JS делает приведение. Сначала false в число (false → 0), затем массив в примитив ([].toString()), в итоге получается пустая строка. А пустая строка и 0 — это true.

Следующий пример:

[] == ''      // true
Типы данных в JavaScript и оператор typeof

Здесь у нас объект приводится к примитиву, снова получается пустая строка, которая сравнивается с пустой же строкой — и в итоге мы получаем true.

А вот здесь пустая строка приводится к числу 0, а два числа 0 дают true:

0 == ''      // true
Типы данных в JavaScript и оператор typeof

👉 Очень важно всегда проверять, какие типы вы сравниваете. И по возможности использовать строгое сравнение (===), которое не допускает преобразований. Вообще, если видите в коде ==, задайте себе вопрос: точно ли здесь должно происходить приведение типов? Если нет — замените на ===.

Алгоритмы сравнения

А, собственно, почему всё так? Почему при приведении false становится числом, а объект — строкой? Когда вы пишете ==, движок JavaScript запускает алгоритм абстрактного сравнения — со множеством шагов, описанный в спецификации ECMAScript.

Упрощённо это работает так:

  • Если типы одинаковые — сравниваются значения.
  • Если один из операндов — null, а другой — undefined, результат true.
  • Если один — строка, а другой число, то строка превращается в число.
  • Если один — boolean, то он приводится к числу (true → 1, false → 0).
  • Если один — объект, а другой примитив, то объект приводится к примитиву через valueOf() или toString().

JS не творит дичь просто так. Он строго следует формализованным правилам, которые местами выглядят как шутка из 90-х, но формально описаны и работают одинаково во всех движках.

Обработка реальных ситуаций

Теперь разберём три самых частых случая, где типы могут вызывать баги.

В реальной жизни данные приходят отовсюду: из форм, из API, из localStorage, из JSON — и чаще всего приходят не в том виде, в каком вы их ожидали. Именно поэтому важно понимать, как и где типы данных влияют на логику приложения.

Обработка данных формы

HTML-форма — это фабрика строк. Что бы пользователь ни выбрал, ни ввёл, ни отметил галочкой — вы получите строку. Даже если это выглядит как число:

Допустим, у нас есть форма, куда пользователь вводит возраст:

<input type="number" name="age" value="30" />

Дальше мы обрабатываем данные из формы:

const formData = new FormData(form);
const age = formData.get('age');
console.log(typeof age); // 'string'

И да, даже если пользователь ввёл 30, typeof age будет ‘string’. И если сразу начнёте с этим числом что-то делать, не проверив и не приведя тип, будут ошибки.

Поэтому данные явно приводим к нужному нам типу, в данном случае к числу:

const age = Number(formData.get('age'));

Обработка данных JSON

JSON по сути просто строка, из которой JSON.parse() делает объекты. Но и тут есть нюансы.

Допустим, мы получаем объект с возрастом и ролями пользователей:

const data = '{"age": "30", "isAdmin": "false"}';
const parsed = JSON.parse(data);
console.log(typeof parsed.age);     // 'string'
console.log(typeof parsed.isAdmin); // 'string'

И сюрприз: даже булево значение пришло строкой. Почему? Потому что JSON по спецификации не знает про булевы строки типа “false”. Он знает только про настоящие true и false без кавычек. А значит, кто-то на бэкенде криво сериализовал данные и решил, что “false” в кавычках — это нормально.

Дальше возникает баг:

if (parsed.isAdmin) {
  // и оно срабатывает, потому что непустая строка — true
}

Получается, по данным человек не админ, но условие срабатывает.

Поэтому сначала делаем строгое сравнение:

const isAdmin = parsed.isAdmin === 'true';
// теперь isAdmin — это boolean: true или false

И уже с ним работаем как с нормальной булевой переменной:

if (isAdmin) {
  // сработает, только если реально true
}

А если таких полей много, то лучше написать отдельную функцию:

function normalizeUser(data) {
  return {
     // '30' → 30 (number)
    age: Number(data.age),
     // 'true'/'false' → boolean                  
    isAdmin: data.isAdmin === 'true',        
    // можно добавить и другие поля
  };
}
const user = normalizeUser(parsed);

JSON — это всегда зона риска. Даже если в документации написано, что isAdmin — булево значение, на практике могут прислать “true”, “1”, “да” или просто null. Поэтому никогда нельзя не доверять данным, даже если их отправляете сами.

Интерпретация ввода пользователя

Та же история с чекбоксами — они хоть и выглядят как true/false, но возвращают не булево значение, а ‘on’, null или вообще ‘1’, если форма нестандартная.

Например есть такой чекбокс, где нужно поставить галочку на согласие:

<form id="myForm">
  <input type="checkbox" name="agree">
</form>

Если мы поставим галку, нажмём «Отправить» и выведем тип значения в консоль, то увидим, что это строка:

   // Получаем ссылку на форму по id
   const form = document.getElementById('myForm');
   // Вешаем обработчик отправки формы
   form.addEventListener('submit', (e) => {
     // Отменяем стандартную отправку формы (без перезагрузки страницы)
     e.preventDefault();
     // Создаём объект FormData — он собирает все данные из формы
     const formData = new FormData(form);
     // Получаем значение чекбокса по имени "agree"
     const value = formData.get('agree');
     // Если галка стоит — 'on', если нет — null
     console.log('Значение:', value);
     // 'string' если 'on', 'object' если null (да, typeof null === 'object')
     console.log('Тип:', typeof value);
   });
Типы данных в JavaScript и оператор typeof

Если чекбокс не отмечен, formData.get(‘agree’) вернёт null. Если отмечен, то вернёт строку ‘on’. Да-да, не true, не 1, а именно ‘on’, потому что это дефолтное поведение HTML.

А теперь представим, что мы пишем что-то вроде:

if (formData.get('agree')) {
  // пользователь согласен
}

И вроде всё норм пока значение ‘on’. Но если внезапно прилетает ‘1’, ‘yes’ или ‘true’, то где-то там в глубине формочка мутировала, и теперь вся логика может сломаться.

Поэтому, если мы хотим просто получить true/false, используем явное преобразование:

const isChecked = Boolean(formData.get('agree'));

А если хотим убедиться, что значение строго ‘on’, то сравниваем явно:

const isChecked = formData.get('agree') === 'on';

Распространённые ошибки и ловушки

Работая с типами в JavaScript, легко попасть в ловушку. В этом блоке разберём наиболее частые грабли: NaN, null и определение массивов и объектов.

Тип NaN и его особенности

Сокращение NaN означает Not-a-Number — но при этом это тип number:

typeof NaN; // 'number'
Типы данных в JavaScript и оператор typeof

Что ещё абсурднее, NaN не равен даже сам себе:

NaN === NaN; // false
Типы данных в JavaScript и оператор typeof

Поэтому этот тип проверяют через специальную функцию:

Number.isNaN(value); // правильный способ

Всё это особенно опасно, когда вы парсите числовые значения из ввода пользователя или API. Например, ждёте price, а туда прилетело undefined или ‘abc’ — вы делаете Number(price) и получаете NaN. И дальше всё сломается:

if (price > 100) { ... } // и тут всё очень плохо

Или, например, при вычислениях:

const discount = total - Number(couponValue); 

Если couponValue не число, вы получаете NaN, а потом по цепочке всё превращается в NaN: total → NaN → finalPrice → NaN.

Так что, если в проекте есть хоть какие-то числовые операции с вводом пользователя, всегда валидируйте и проверяйте на NaN через Number.isNaN(), прежде чем идти дальше.

Тип null и его особенности

null — это отдельный примитив, означающий намеренное отсутствие значения. Но вот как мы уже писали, по историческим причинам null — это object. А ещё null легко путается с undefined, особенно если использовать нестрогое сравнение:

null == undefined; // true — особое правило
null === undefined; // false — строго, как надо
Типы данных в JavaScript и оператор typeof

Важно запомнить, что undefined — это «значение не задано», а null — «значение задано как отсутствующее». Разница может быть тонкой, но критичной.

Теперь к реальному сценарию. Допустим, вы получаете данные от бэкенда, где фамилия пользователя может быть null, undefined, пустой строкой или ‘0’ (для тестовых случаев) — и пишете:

if (!user.lastName) {
  showWarning('Фамилия не указана');
}

На первый взгляд работает. Но if — это не == и не ===. Он использует логическое приведение к true/false. В результате условие срабатывает и там, где не должно — и валидная фамилия ‘0’ (например, у пользователя из тестовой базы) отклоняется как «не указана».

Поэтому важно использовать строгую проверку:

if (user.lastName === undefined || user.lastName === null || user.lastName === '') {
  showWarning('Фамилия не указана');
}

В общем, null и undefined — это не просто «ничего». Это два разных «ничего», и обращаться с ними нужно осторожно, особенно когда дело касается данных из API, форм и фильтров. И всегда уточняйте, что именно вы ждёте от поля: оно отсутствует? Оно задано, но пустое? Или оно = 0?

Типы для массивов и объектов

В JavaScript массив — это частный случай объекта, только у него есть числовые ключи и встроенные методы. Но для движка это всё равно object:

typeof [] // 'object'
Типы данных в JavaScript и оператор typeof

Допустим, мы пишем обёртку над API, откуда приходит поле items:

if (typeof items === 'object') {
  items.forEach(item => doSomething(item));
}

Разработчик думает: items — во множественном числе, значит, наверное, массив. А typeof говорит ‘object’ — ну вроде сходится, можно итерировать.

Но в рантайме вылетает TypeError: items.forEach is not a function. А всё потому, что items оказался не массивом, а обычным объектом. А может, это вообще null, у которого typeof тоже ‘object’.

Тип object может быть чем угодно: {}, [], null, даже new Date(). Поэтому если мы хотим работать с массивом, то нужно явно спрашивать, что перед нами такое:

if (Array.isArray(items)) {
  items.forEach(...);
} else {
  // делаем, если это не массив
}

Array.isArray() — это единственный надёжный способ отличить массив от других объектов.

 Это важно, поскольку почти в любом приложении вам придётся обрабатывать JSON, где может быть либо [], либо {}, проверять входные данные из форм и работать с данными из сторонних библиотек или fetch-запросов. И если тупо проверять typeof, не различая массив и объект, — будут баги.

Вам слово

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

Обложка:

Алексей Сухов

Корректор:

Александр Зубов

Вёрстка:

Егор Степанов

Соцсети:

Юлия Зубарева

Вам может быть интересно
easy