TypeScript and StorageManager

TypeScript and StorageManager

This article explains TypeScript and StorageManager.

We provide practical examples to explain TypeScript and StorageManager.

YouTube Video

TypeScript and StorageManager

What is StorageManager?

StorageManager is an API that allows web applications to estimate how much storage they are using and how much capacity is available. It also provides a persistence request (persist()) to prevent automatic data removal by the user or browser. This allows you to control whether there is enough free space or if data will be automatically deleted, such as when storing large amounts of data in IndexedDB for offline applications.

Feature Detection

First, check whether the browser supports navigator.storage.

 1// TypeScript: feature detection for StorageManager
 2function supportsStorageManager(): boolean {
 3  return typeof navigator !== "undefined" &&
 4         typeof navigator.storage !== "undefined" &&
 5         typeof navigator.storage.estimate === "function";
 6}
 7
 8// Example usage
 9if (supportsStorageManager()) {
10  console.log("StorageManager is supported.");
11} else {
12  console.warn("StorageManager not supported. Falling back to localStorage or IndexedDB.");
13}
  • This function checks the availability of the StorageManager API in stages.
  • Because navigator.storage does not exist in some browsers like Safari, it is important to check its existence safely using typeof.

Estimate Storage Usage (estimate())

After checking for functionality with supportsStorageManager(), obtain the usage (usage) and quota (quota) from navigator.storage.estimate().

 1// TypeScript: safely get storage estimate
 2async function getStorageEstimate(): Promise<{ usage: number; quota: number } | null> {
 3  if (!supportsStorageManager()) {
 4    console.warn("StorageManager not supported.");
 5    return null;
 6  }
 7
 8  const estimate = await navigator.storage.estimate();
 9  return { usage: estimate.usage ?? 0, quota: estimate.quota ?? 0 };
10}
11
12// Example usage
13getStorageEstimate().then(result => {
14  if (result) {
15    console.log(`Usage: ${result.usage} bytes / Quota: ${result.quota} bytes`);
16  }
17});
  • This function always works safely and returns null if the browser does not support it.
  • usage and quota are estimates and may vary between browsers.

Request Data Persistence (persist())

Use persist() to request persistence so that important data (e.g., offline cache) is not subject to automatic deletion by the browser. However, it does not succeed in all environments.

 1// TypeScript: safely request persistent storage
 2async function requestPersistence(): Promise<boolean> {
 3  if (!supportsStorageManager()) {
 4    console.warn("StorageManager not supported.");
 5    return false;
 6  }
 7
 8  if (typeof navigator.storage.persist !== "function") {
 9    console.warn("persist() not available in this browser.");
10    return false;
11  }
12
13  try {
14    const granted = await navigator.storage.persist();
15    return Boolean(granted);
16  } catch (err) {
17    console.error("persist() error:", err);
18    return false;
19  }
20}
21
22// Example usage
23requestPersistence().then(granted => {
24  console.log("Persistence granted?", granted);
25});
  • If persist() succeeds, the data will not be automatically deleted by the browser, except by manual user intervention. However, requests can be rejected depending on user actions or browser settings.

Check Available Storage Before Saving Data

Verify available space before saving large data to prevent write failures (QuotaExceededError).

 1// TypeScript: ensure enough space before writing
 2async function ensureSpaceAndWrite(neededBytes: number, writeFn: () => Promise<void>): Promise<boolean> {
 3  const estimate = await getStorageEstimate();
 4  if (!estimate) {
 5    console.warn("Cannot check storage space. Proceeding without validation.");
 6    await writeFn();
 7    return true;
 8  }
 9
10  const free = estimate.quota - estimate.usage;
11  if (free < neededBytes) {
12    console.warn(`Not enough space. Free: ${free} bytes, needed: ${neededBytes} bytes.`);
13    return false;
14  }
15
16  await writeFn();
17  return true;
18}
19
20// Example usage
21ensureSpaceAndWrite(10 * 1024 * 1024, async () => {
22  console.log("Saving large data...");
23});
  • By checking available storage before saving, you can reduce the risk of write interruptions due to insufficient capacity.

Handling localStorage Type-Safely in TypeScript

localStorage is convenient for storing lightweight configuration data and similar information.

The following class uses generics to create a type-safe wrapper.

 1// TypeScript: typed localStorage wrapper
 2type Serializer<T> = {
 3  serialize: (v: T) => string;
 4  deserialize: (s: string) => T;
 5};
 6
 7class TypedLocalStorage<K extends string, V> {
 8  constructor(private storage: Storage, private serializer: Serializer<V>) {}
 9
10  set(key: K, value: V): void {
11    this.storage.setItem(key, this.serializer.serialize(value));
12  }
13
14  get(key: K): V | null {
15    const raw = this.storage.getItem(key);
16    if (raw === null) return null;
17    try {
18      return this.serializer.deserialize(raw);
19    } catch {
20      return null;
21    }
22  }
23
24  remove(key: K): void {
25    this.storage.removeItem(key);
26  }
27
28  clear(): void {
29    this.storage.clear();
30  }
31}
32
33// Example usage
34const jsonSerializer: Serializer<any> = {
35  serialize: v => JSON.stringify(v),
36  deserialize: s => JSON.parse(s),
37};
38
39const appStorage = new TypedLocalStorage<'theme' | 'token', any>(localStorage, jsonSerializer);
40appStorage.set('theme', { dark: true });
41console.log(appStorage.get('theme'));
  • This class helps reduce the risk of storing or retrieving data with the wrong type.

Practical Considerations and Best Practices

  • Always check if the browser supports StorageManager Since StorageManager is a relatively new API, always confirm its existence before use.

  • Pay attention to storage limits and compatibility The value returned by estimate() varies by browser and is only an estimate.

  • Design with Asynchronous APIs in Mind estimate() and persist() are promise-based asynchronous APIs. Design your code so that you don't block the UI.

  • Security Considerations Do not store sensitive information such as access tokens in localStorage. Consider using HttpOnly cookies or encryption if possible.

  • Fallback Design If supportsStorageManager() returns false, provide a logic to switch to using only localStorage or IndexedDB.

Summary

With feature detection using supportsStorageManager(), you can safely support all environments, check usage and quota with navigator.storage.estimate(), and request persistence with persist(). In TypeScript, use wrapper functions and type definitions to write readable and safe code. The basic flow is: check for existence, estimate, request persistence, and check capacity before writing to storage.

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video