Type Local Storage in TypeScript
Learn how TypeScript types the localStorage API. Understand the Storage interface, string-only values, null handling, and patterns for typed get/set wrappers.
localStorage is a browser API that stores key-value pairs as strings and persists them across browser sessions. TypeScript types it through the Storage interface, which is the same interface used by sessionStorage. The type system tells you that every key and every value is a string, but it does not know what data you store behind each key. You handle that with your own type annotations.
How TypeScript types localStorage
The global localStorage variable is typed as Storage. This interface gives you methods for reading, writing, and removing items, plus a length property for the total count. The key methods and their TypeScript signatures are straightforward.
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
// ^? const theme: string | null
if (theme !== null) {
console.log(theme.toUpperCase()); // "DARK"
}getItem returns string or null because the key might not exist in storage. This is different from an empty string, which means the key exists but was set to an empty value. The null forces you to handle the missing-key case explicitly, which is the right default for storage that can be cleared at any time.
Storing and retrieving non-string data
Storage only handles strings. To store numbers, booleans, or objects, you convert them yourself. TypeScript cannot verify the conversion at compile time, so you write the serialisation and parsing code with clear type expectations.
interface UserPreferences {
theme: string;
fontSize: number;
}
const prefs: UserPreferences = { theme: 'dark', fontSize: 16 };
localStorage.setItem('prefs', JSON.stringify(prefs));
const stored = localStorage.getItem('prefs');
if (stored !== null) {
const parsed: UserPreferences = JSON.parse(stored);
console.log(parsed.fontSize); // 16
}JSON.parse returns any by default. The type annotation on parsed tells TypeScript what shape you expect. This is a compile-time assertion, not a runtime validation. If someone manually edits localStorage and puts invalid JSON under the "prefs" key, JSON.parse will throw.
Always wrap JSON.parse in try-catch when reading from storage, because the data could be corrupted or missing:
function loadPreferences(): UserPreferences | null {
const stored = localStorage.getItem('prefs');
if (stored === null) return null;
try {
const parsed: UserPreferences = JSON.parse(stored);
return parsed;
} catch {
localStorage.removeItem('prefs');
return null;
}
}The try-catch catches invalid JSON, and removing the corrupted key prevents the same error on the next read. Returning null from the function signals to the caller that no valid preferences were found.
A typed storage helper
Instead of repeating null checks and try-catch blocks, wrap the Storage API in a small typed helper. This keeps the type safety in one place and makes the rest of your code cleaner.
function loadFromStorage<T>(key: string, fallback: T): T {
const stored = localStorage.getItem(key);
if (stored === null) return fallback;
try {
return JSON.parse(stored) as T;
} catch {
localStorage.removeItem(key);
return fallback;
}
}The generic type parameter T lets the caller specify the expected type once, and the fallback value provides a safe default when the key is missing or corrupted. The helper handles null, parse errors, and cleanup in one place. Call it with a type argument and a fallback object matching that type:
const prefs = loadFromStorage<UserPreferences>('prefs', {
theme: 'light',
fontSize: 14,
});Removing and clearing items
The Storage interface provides removeItem for deleting a single key and clear for removing everything. Both are straightforward and type-safe without any extra work:
localStorage.removeItem('theme');
localStorage.clear();After calling clear, every subsequent getItem call returns null. The length property drops to zero. These methods never throw, so you do not need try-catch around them.
Storage event typing
The browser fires a StorageEvent on other tabs when localStorage changes in the current tab. TypeScript types this event through the StorageEvent interface, which gives you the key, old value, and new value of the change.
window.addEventListener('storage', (event) => {
// ^? (parameter) event: StorageEvent
console.log(event.key); // the key that changed
console.log(event.oldValue); // previous value (string or null)
console.log(event.newValue); // new value (string or null)
});The event does not fire in the tab that made the change, only in other tabs from the same origin. StorageEvent.key and the value properties are all typed as string or null, consistent with the Storage interface.
Common mistakes
Forgetting to handle null from getItem is the most frequent error. TypeScript flags this at compile time because the return type includes null in the union. Avoid the temptation to use a non-null assertion unless you are certain the key exists.
Storing objects without JSON.stringify stores the string "[object Object]" instead of the actual data. TypeScript cannot catch this because setItem accepts any string, and calling toString on an object produces a useless string silently.
Not wrapping JSON.parse in try-catch leads to runtime crashes when storage data is corrupted. localStorage is shared across all code on the same origin, so another script or a browser extension could modify or corrupt your data.
See Type Dataset Values in TypeScript for similar string-only DOM storage patterns. See Type Fetch in the Browser with TypeScript for typing API responses that you might want to cache in localStorage.
Rune AI
Key Insights
- localStorage is typed as Storage, with all keys and values as strings.
- getItem returns string or null, so always check for null before using the result.
- Use JSON.stringify and JSON.parse with type assertions for non-string data.
- Wrap JSON.parse in try-catch to handle corrupted or missing data.
- A typed helper function reduces repetition and catches errors in one place.
Frequently Asked Questions
What type does localStorage have in TypeScript?
How do I store objects in localStorage with TypeScript?
Why does localStorage.getItem return string or null?
Conclusion
TypeScript types localStorage through the Storage interface, where every key and value is a string. Use JSON serialization for objects, handle null from getItem, and wrap unsafe operations in try-catch. A small typed wrapper function makes repeated storage access safer and cleaner.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.