Type safe local storage utils
Jul 29, 2025
Setter
Validate the value with the key’s Zod schema and store a JSON‑serialized version in localStorage.
export function setLocalStorageItem<K extends LocalStorageKey>(
key: K,
value: LocalStorageValue<K>
): void {
try {
const schema = LOCAL_STORAGE_SCHEMAS[key];
const validationResult = schema.safeParse(value);
if (!validationResult.success) {
console.error(
`[LocalStorageError] Type mismatch or invalid value for key "${key}". ` +
`Provided value does not conform to its schema.`,
validationResult.error.issues
);
// Depending on strictness, you might throw an error here, or just log and return.
// For localStorage, it's often better to prevent storing bad data.
return;
}
// maybe strip extra properties if the schema is strict?
const serializedValue = JSON.stringify(validationResult.data);
localStorage.setItem(key, serializedValue);
} catch (error) {
console.error(
`[LocalStorageError] Failed to set item for key "${key}":`,
error
);
}
}Getter
Parse JSON from localStorage, validate with the key’s schema, and return the value or a safe default if parsing/validation fails.
export function getLocalStorageItem<K extends LocalStorageKey>(
key: K,
defaultValue?: LocalStorageValue<K>
): LocalStorageValue<K> | undefined {
const schema = LOCAL_STORAGE_SCHEMAS[key];
// Try to get the stored value
const serializedValue = localStorage.getItem(key);
// If no stored value, try to use default or schema default
if (serializedValue === null) {
// Validate provided default against schema
if (defaultValue !== undefined) {
const defaultResult = schema.safeParse(defaultValue);
if (defaultResult.success) {
return defaultResult.data;
}
console.error(
`[LocalStorageError] Default value for key "${key}" does not conform to schema:`,
defaultResult.error.issues
);
}
// Try schema default as fallback
const schemaDefaultResult = schema.safeParse(undefined);
return schemaDefaultResult.success ? schemaDefaultResult.data : undefined;
}
let parsedValue: unknown;
try {
parsedValue = JSON.parse(serializedValue);
} catch (error) {
console.error(
`[LocalStorageError] Failed to parse stored value for key "${key}":`,
error
);
return getLocalStorageItem(key, defaultValue); // Recursively try with default
}
const validationResult = schema.safeParse(parsedValue);
if (validationResult.success) {
return validationResult.data;
}
console.warn(
`[LocalStorageValidation] Stored data for key "${key}" is invalid:`,
validationResult.error.issues
);
if (defaultValue !== undefined) {
const defaultResult = schema.safeParse(defaultValue);
if (defaultResult.success) {
return defaultResult.data;
}
console.error(
`[LocalStorageError] Default value for key "${key}" does not conform to schema:`,
defaultResult.error.issues
);
}
// Try schema default as last resort
const schemaDefaultResult = schema.safeParse(undefined);
return schemaDefaultResult.success ? schemaDefaultResult.data : undefined;
}