Булевы значения и null

localForage хранит данные через драйверы браузера (IndexedDB, WebSQL, localStorage), абстрагируя различия между ними и предоставляя единый API. В результате типизация значений в значительной степени сохраняется, в отличие от классического localStorage, где всё приводится к строке.

Булевы значения (true и false) являются базовым примитивом, который корректно поддерживается:

await localforage.setItem('featureEnabled', true);
const value = await localforage.getItem('featureEnabled');
// value === true

При чтении значение возвращается в исходном типе, без приведения к строке, что принципиально отличает localForage от стандартного Web Storage API.

Поведение при разных драйверах

  • IndexedDB: сохраняет значение как structured clone, тип сохраняется полностью
  • WebSQL: хранит сериализованные данные, тип восстанавливается при чтении
  • localStorage (fallback): происходит JSON-сериализация, булевы значения восстанавливаются через JSON.parse

Таким образом, вне зависимости от драйвера, булев тип остаётся булевым, если не нарушены ограничения сериализации.

Логическая интерпретация значений

Особенность булевых значений проявляется при проверках:

const enabled = await localforage.getItem('featureEnabled');

if (enabled) {
  // выполняется только если true
}

Здесь важно учитывать, что false корректно возвращается и не эквивалентен отсутствию значения, однако в логических условиях он ведёт себя как falsy, что может маскировать факт наличия сохранённого значения.


null в localForage

null в localForage имеет более сложную семантику, чем булевы значения, поскольку пересекается с понятием “отсутствие данных”.

Сохранение null

localForage позволяет явно сохранять null:

await localforage.setItem('token', null);
const value = await localforage.getItem('token');
// value === null

Это важное отличие от undefined, которое не поддерживается и фактически игнорируется или преобразуется в отсутствие значения в зависимости от драйвера.

Двойственная природа null

Проблема возникает из-за того, что getItem возвращает null в двух случаях:

  1. Ключ отсутствует
  2. Ключ существует, и его значение равно null
const value = await localforage.getItem('missingKey');
// null

await localforage.setItem('existingKey', null);
const value2 = await localforage.getItem('existingKey');
// null

Таким образом, null теряет способность однозначно различать состояние “нет значения” и “значение установлено в null”.


Проверка существования ключа

Из-за неоднозначности null требуется отдельная стратегия проверки наличия данных.

Использование keys()

const keys = await localforage.keys();
const exists = keys.includes('userToken');

Этот подход позволяет точно определить, существует ли запись, независимо от её значения.

Использование removeItem как семантики отсутствия

Часто null не используют как значение, а вместо этого применяют удаление:

await localforage.removeItem('userToken');

После этого ключ исчезает полностью, и getItem возвращает null, но уже в единственном смысле — отсутствие записи.


Булевы значения и логические ловушки

Проблема falsy-логики

Булевы значения часто участвуют в условиях, что может приводить к ошибкам:

const value = await localforage.getItem('flag');

if (value) {
  // true
}

Если value === false, условие не выполнится, хотя значение существует и было явно задано.

Корректная проверка:

if (value === true) {
  // строгое включение
}

или

if (value !== null) {
  // значение существует (включая false)
}

Комбинация boolean и null в конфигурациях

В реальных сценариях булевы значения часто комбинируются с null как состоянием “не задано”:

  • true — включено
  • false — отключено
  • null — не инициализировано
await localforage.setItem('darkMode', null);

const theme = await localforage.getItem('darkMode');

if (theme === null) {
  // применить системную тему
} else if (theme === true) {
  // тёмная тема
} else {
  // светлая тема
}

Такой подход формирует трёхсостоянийную логику, где null играет роль отдельного состояния, а не отсутствия данных.


Особенности сериализации

localForage использует сериализацию данных, поэтому важно учитывать:

  • true и false сериализуются без потери типа
  • null сериализуется как JSON null или structured clone null
  • undefined не поддерживается и теряется
await localforage.setItem('a', undefined);
const value = await localforage.getItem('a');
// value === null

Это поведение может приводить к путанице, если смешивать null и undefined в логике приложения.


Сравнение с localStorage

В классическом localStorage всё хранится как строки:

localStorage.setItem('flag', true);
console.log(localStorage.getItem('flag')); // "true"

В localForage:

await localforage.setItem('flag', true);
const value = await localforage.getItem('flag'); // boolean true

Разница критична при работе с булевыми значениями и null, поскольку исчезает необходимость ручного JSON.parse и JSON.stringify.


Практические паттерны работы с состояниями

Явное состояние конфигурации

const settings = {
  notifications: true,
  autoSave: false,
  backupMode: null
};

for (const [key, value] of Object.entries(settings)) {
  await localforage.setItem(key, value);
}

Безопасное чтение с дефолтами

const notifications = await localforage.getItem('notifications');

const normalized = notifications === null
  ? true
  : notifications;

Разделение “не задано” и “выключено”

const feature = await localforage.getItem('betaFeature');

const isEnabled =
  feature === true; // строгое включение

const isDefined =
  feature !== null; // существует ли настройка

Поведение при миграциях данных

При изменении структуры данных булевы значения и null часто используются как индикаторы миграции:

const version = await localforage.getItem('schemaVersion');

if (version === null) {
  await localforage.setItem('schemaVersion', 1);
}

Такой подход позволяет различать:

  • отсутствие схемы
  • явно установленную версию

без необходимости дополнительных ключей-флагов.


Итоговые особенности семантики

Комбинация булевых значений и null в localForage формирует модель хранения, где:

  • булевы значения сохраняют строгую типизацию
  • null выступает как допустимое значение и как маркер отсутствия
  • отсутствует различие между “нет ключа” и “значение null”
  • требуется явная логика проверки существования при сложных сценариях

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