make-typed-storage
v0.0.1
Published
Type-safe Web Storage for TypeScript. Schema-agnostic via Standard Schema.
Maintainers
Readme
make-typed-storage
Type-safe Web Storage for TypeScript. Validate with any schema library, get full autocomplete and type errors at compile time.
Features
🛡️ Schema-validated reads and writes — catch invalid data before it corrupts your storage.
🔮 Fully typed accessors — autocomplete on keys, type errors on wrong values, zero manual annotations.
🔌 Works with any Standard Schema library — Zod, Valibot, ArkType, and more.
🗄️ Works with localStorage, sessionStorage, or any custom StorageLike backend.
🎯 Structured error handling — StorageValidationError with the key that failed and all issues.
🪶 Zero runtime dependencies.
Install
npm install make-typed-storageThe problem
localStorage.setItem("prefs", JSON.stringify({ theme: "dark", lang: "en" }));
const raw = localStorage.getItem("prefs");
const prefs = raw ? JSON.parse(raw) : null;
// ^? any — no type safety, no autocomplete
prefs.thmee;
// No error — typo in key, silently returns undefined
localStorage.setItem("prefs", JSON.stringify({ theme: 42 }));
// No error — wrong type, corrupted dataThe solution
import { makeTypedStorage } from "make-typed-storage";
import { z } from "zod";
const prefs = makeTypedStorage(
"prefs",
z.object({ theme: z.enum(["light", "dark"]), lang: z.string() }),
localStorage,
);
prefs.get("theme");
// ^? "light" | "dark" — correct type, full autocomplete
prefs.get("thmee");
// ~~~~~~ — Type error: "thmee" is not a valid key
prefs.setAll({ theme: 42, lang: "en" });
// ~~ — Throws StorageValidationErrorUsage
Reading from storage
const theme = prefs.get("theme");
// ^? "light" | "dark" | undefined
const all = prefs.getAll();
// ^? { theme: "light" | "dark"; lang: string } | undefinedget reads without validation (fast path). getAll validates and returns undefined if the stored data is invalid.
Validated reads with strictGet
const theme = prefs.strictGet("theme");
// ^? "light" | "dark"strictGet validates the entire stored object before returning the value. Throws StorageValidationError if the data is invalid.
Writing to storage
prefs.set("theme", "dark");
prefs.setAll({ theme: "dark", lang: "en" });set writes a single key without validation — useful for building up state incrementally. setAll validates the entire object before writing.
Partial updates with merge
prefs.setAll({ theme: "dark", lang: "en" });
prefs.merge({ lang: "pt" });
// Storage now contains: { theme: "dark", lang: "pt" }merge reads existing data, shallow-merges the new partial, validates the result, and writes it back.
Clearing storage
prefs.clear();
prefs.isSet; // falseUsing a custom storage backend
makeTypedStorage works with any object implementing StorageLike:
import { makeTypedStorage } from "make-typed-storage";
const storage: StorageLike = {
getItem: (key) => cookies.get(key) ?? null,
setItem: (key, value) => cookies.set(key, value),
removeItem: (key) => cookies.delete(key),
};
const prefs = makeTypedStorage("prefs", schema, storage);Convenience functions
For the two most common backends:
import { makeLocalStorage, makeSessionStorage } from "make-typed-storage";
const prefs = makeLocalStorage("prefs", schema);
const session = makeSessionStorage("session", schema);Error handling
Validating methods throw StorageValidationError on failure, which includes the storage key and structured issues:
import { StorageValidationError } from "make-typed-storage";
try {
prefs.strictGet("theme");
} catch (error) {
if (error instanceof StorageValidationError) {
console.log(error.storageKey); // "prefs"
console.log(error.issues); // [{ message: "Expected string, received number" }]
}
}Error messages are descriptive:
Storage "prefs" validation failed:
- Expected string, received numberSchema libraries
make-typed-storage works with any library that implements the Standard Schema spec.
Zod
import { z } from "zod";
const prefs = makeTypedStorage(
"prefs",
z.object({ theme: z.enum(["light", "dark"]), lang: z.string() }),
localStorage,
);Valibot
import * as v from "valibot";
const prefs = makeTypedStorage(
"prefs",
v.object({ theme: v.picklist(["light", "dark"]), lang: v.string() }),
localStorage,
);ArkType
import { type } from "arktype";
const prefs = makeTypedStorage(
"prefs",
type({ theme: "'light' | 'dark'", lang: "string" }),
localStorage,
);API
makeTypedStorage(storageKey, schema, storage)
Create a type-safe storage wrapper.
| Parameter | Type | Description |
| ------------ | ------------------------------ | ---------------------------------------------------- |
| storageKey | string | The key used in the storage backend |
| schema | StandardSchemaV1<unknown, T> | Any Standard Schema compatible schema |
| storage | StorageLike | Storage backend (localStorage, sessionStorage, etc.) |
Returns a TypedStorage<T> instance.
makeLocalStorage(storageKey, schema)
Convenience wrapper that uses globalThis.localStorage.
makeSessionStorage(storageKey, schema)
Convenience wrapper that uses globalThis.sessionStorage.
TypedStorage<T>
| Method | Returns | Validates | Throws on failure |
| ----------------- | ------------------- | --------- | ------------------------ |
| get(key) | T[K] \| undefined | No | — |
| strictGet(key) | T[K] | Yes | StorageValidationError |
| set(key, value) | void | No | — |
| setAll(data) | void | Yes | StorageValidationError |
| getAll() | T \| undefined | Yes | — (returns undefined) |
| merge(data) | void | Yes | StorageValidationError |
| clear() | void | No | — |
| isSet | boolean | No | — |
| toJSON() | T \| undefined | Yes | — (returns undefined) |
StorageValidationError
| Property | Type | Description |
| ------------ | --------------------------------------- | --------------------------- |
| storageKey | string | The storage key that failed |
| issues | ReadonlyArray<StandardSchemaV1.Issue> | Validation issues |
| message | string | Formatted error message |
StorageLike
interface StorageLike {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
}Errors
- Throws
StorageValidationErrorwhen data fails schema validation - Throws
TypeErrorif an async schema is passed (storage operations are synchronous)
