type StorageType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null'
interface StorageData<T = unknown> {
type: StorageType
data: T
}
const STORAGE_TYPE_MAP = {
string: 'string',
number: 'number',
boolean: 'boolean',
null: 'null',
object: 'object',
array: 'array'
} as const
// 类型守卫验证函数
function isValidStorageData(data: any): data is StorageData {
return data && typeof data === 'object' && 'type' in data && 'data' in data
}
class TypedStorage {
// 类型安全的存储方法
static setItem<T>(key: string, value: T): void {
let type: StorageType = STORAGE_TYPE_MAP.string
if (value === null) {
type = STORAGE_TYPE_MAP.null
} else if (Array.isArray(value)) {
type = STORAGE_TYPE_MAP.array
} else if (typeof value === 'object') {
type = STORAGE_TYPE_MAP.object
} else if (typeof value === 'number') {
type = STORAGE_TYPE_MAP.number
} else if (typeof value === 'boolean') {
type = STORAGE_TYPE_MAP.boolean
}
const storageData: StorageData = {
type,
data: value as Exclude<T, Function> // 过滤函数类型
}
localStorage.setItem(key, JSON.stringify(storageData))
}
// 类型安全的读取方法(带自动类型推断)
static getItem<T>(key: string): T | null {
const storedValue = localStorage.getItem(key)
if (!storedValue) return null
try {
const parsedData = JSON.parse(storedValue)
if (!isValidStorageData(parsedData)) {
return storedValue as unknown as T // 兼容原生字符串
}
switch (parsedData.type) {
case STORAGE_TYPE_MAP.number:
return Number(parsedData.data) as T
case STORAGE_TYPE_MAP.boolean:
return Boolean(parsedData.data) as T
case STORAGE_TYPE_MAP.null:
return null as T
case STORAGE_TYPE_MAP.object:
case STORAGE_TYPE_MAP.array:
return parsedData.data as T
default:
return parsedData.data as T
}
} catch {
return storedValue as unknown as T // 解析失败返回原始字符串
}
}
// 其他原生方法保持类型安全
static removeItem(key: string): void {
localStorage.removeItem(key)
}
static clear(): void {
localStorage.clear()
}
}
// 使用示例
interface User {
name: string
age: number
}
// 存储复杂对象
TypedStorage.setItem<User>('user', { name: 'Alice', age: 28 })
// 读取时自动推断类型
const user = TypedStorage.getItem<User>('user') // 类型为 User | null
console.log(user?.age.toFixed(2)) // 无需类型断言
// 存储基础类型
TypedStorage.setItem<number>('counter', 42)
const counter = TypedStorage.getItem<number>('counter') // number | null
// 存储数组
TypedStorage.setItem<number[]>('scores', [90, 85, 95])
const scores = TypedStorage.getItem<number[]>('scores') // number[] | null
主要特性说明:
- 类型自动推断
- 通过泛型参数和类型守卫实现精确的类型推断
- 读取时自动恢复原始数据类型(number/boolean/object等)
- 安全存储结构
- 使用
{ type: StorageType, data: T }
结构存储类型信息 - 自动处理JSON序列化/反序列化
- 使用
- 异常处理
- 兼容原生localStorage存储的普通字符串
- 解析失败时降级返回原始字符串
- 类型过滤
- 使用Exclude<T, Function>过滤函数类型存储
- null值特殊处理避免类型冲突
- 完整API支持
- 保持原生localStorage的API风格(setItem/getItem/removeItem/clear)
使用建议:
// 存储时明确类型
TypedStorage.setItem<YourType>('key', value)
// 读取时指定类型
const value = TypedStorage.getItem<YourType>('key')
// 处理null情况
const value = TypedStorage.getItem<YourType>('key') ?? defaultValue
此方案在保留localStorage原生API特点的同时,通过TypeScript类型系统提供了更安全的类型保障,且自动处理了数据类型转换问题。