localStorage обёртка

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

Задача

Нативный localStorage работает только со строками и бросает исключение в режиме инкогнито или при переполнении. Нужна утилита, которая прозрачно работает с объектами и не ломает приложение при ошибках.

Решение

// utils/storage.ts

function storage<T>(key: string, defaultValue: T) {
  return {
    get: T {
      try {
        const item = localStorage.getItem(key);
        return item !== null ? (JSON.parse(item) as T) : defaultValue;
      } catch {
        return defaultValue;
      }
    },

    set(value: T): void {
      try {
        localStorage.setItem(key, JSON.stringify(value));
      } catch (err) {
        console.warn(`storage.set(${key}) failed:`, err);
      }
    },

    remove: void {
      try {
        localStorage.removeItem(key);
      } catch {
        // silent
      }
    },

    update(updater: (prev: T) => T): void {
      this.set(updater(this.get));
    },
  };
}

export { storage };

Использование:

import { storage } from './utils/storage';

// Настройки пользователя
const themeStorage = storage<'light' | 'dark'>('theme', 'light');
const theme = themeStorage.get; // 'light' если ничего не сохранено

themeStorage.set('dark');

// Массив избранных ID
const favStorage = storage<number>('favorites', );
favStorage.update((prev) => [...prev, 42]); // добавить 42

// Очистка
themeStorage.remove();

Без TypeScript (JavaScript):

const storage = (key, defaultValue) => ({
  get {
    try { return JSON.parse(localStorage.getItem(key)) ?? defaultValue; }
    catch { return defaultValue; }
  },
  set(value) {
    try { localStorage.setItem(key, JSON.stringify(value)); }
    catch (e) { console.warn(e); }
  },
  remove { localStorage.removeItem(key); },
});

Ключевые моменты

  • try/catch обязателен: localStorage недоступен в режиме инкогнито (Safari), при переполнении диска и в некоторых iframe.
  • JSON.parse(null) возвращает null, поэтому проверяй item !== null перед парсингом.
  • Функция update с колбэком — удобный паттерн для иммутабельного изменения сохранённых данных.
  • Фабрика storage(key, default) — каждый ключ получает свой объект, нет опечаток в строках.

Варианты

  • sessionStorage — то же API, но данные удаляются при закрытии вкладки.
  • IndexedDB — для больших объёмов данных и бинарных файлов.
  • idb-keyval — Promise-обёртка над IndexedDB в 600 байт.

Связанные рецепты / темы