🗃️

タイプセーフなStorage API管理 + Svelte

2024/12/31に公開

導入

LocalStorageやSessionStorageといったStorage APIは非常に便利な存在ですが、数値やオブジェクトを格納しようとすると型定義が曖昧になりがちです。
また、いちいちシリアライズ、デシリアライズを噛ませるのもネックになります。
そこで今回は、Storage APIをより便利に扱うラッパーライブラリを作成したので紹介します。

@jill64/typed-storage

https://github.com/jill64/typed-storage

string型

まずは、型指定なし(string型)から説明します。
typedStorageにキーを渡すとゲッターとセッターを作ってくれます。

import { typedStorage } from '@jill64/typed-storage'

const store = typedStorage('local-storage-key')

store.get()

store.set('foo')

Storage APIは変更をサブスクライブできる機能もついているので、イベントリスナーもラップできます。

import { typedStorage } from '@jill64/typed-storage'

const store = typedStorage('local-storage-key')

const unsubscriber = store.addListener((value) => {
    // 'local-storage-key'の値が変更されるとconsoleに出力
    console.log(value)
})

// サブスクライブを止める
unsubscriber()

カスタム型

typedStorageの第二引数にserdeパラメータを渡すことで、string型以外を扱うことができます。serdeパラメーターはseriarizedeserializeメソッドで構成されていて、それぞれの型に応じて定義します。
例えば、number型を扱いたい場合、serdeの簡単な実装は以下のようになります。

const number = {
    serialize: (value: number) => value.toString(),
    desirialize: (value: string) => Number(value)
}

これを使うと以下のようなことができます。

import { typedStorage } from '@jill64/typed-storage'

const store = typedStorage('local-storage-num', {
    serialize: (value: number) => value.toString(),
    desirialize: (value: string) => Number(value)
})

// number型
store.get()

store.set(123)

よく使用されるserdeはあらかじめライブラリに準備されています。

import { typedStorage } from '@jill64/typed-storage'
import { number } from '@jill64/typed-storage/serde'

const store = typedStorage('local-storage-num', number)

これ以外にもenumjsonなど、準備された様々なserdeがあります。
詳細はts-serdeをご覧ください。
(また記事にするかもしれないです)

追記(2025/01/14)

ts-serdeについての記事を書きました。

https://github.com/jill64/ts-serde

SessionStorage

LocalStorageの代わりにSessionStorageを使用することもできます。
第3引数にオプションを渡すだけです。

import { typedStorage } from '@jill64/typed-storage'
import { number } from '@jill64/typed-storage/serde'

const store = typedStorage('local-storage-num', number, {
  sessionStorage: true
})

Svelteでもっと便利に扱いたい!

https://github.com/jill64/svelte-storage

私はSvelteが好きなのでさらにラップするライブラリを作成しました。
こちらはSvelte5からの仕様でAPIが若干異なっています。

import { storage } from '@jill64/svelte-storage'
import { string, number, boolean } from '@jill64/svelte-storage/serde'

const localStorage = storage({
    ['localStorage-key-str']: string,
    ['localStorage-key-num']: number,
    ['localStorage-key-bool']: boolean
})

consol.log(localStorage['localStorage-key-str'])
consol.log(localStorage['localStorage-key-num'])
consol.log(localStorage['localStorage-key-bool'])

localStorage['localStorage-str'] = 'value'
localStorage['localStorage-num'] = 123
localStorage['localStorage-bool'] = true

第1引数はキーとserdeのオブジェクトで構成されます。
この時localStorageが冗長に感じますが、デストラクチャリングをしてはいけません。
なぜならSvelte5のリアクティビティは、値ではなくゲッター経由でないと得られないからです。

import { storage } from '@jill64/svelte-storage'
import { string, number, boolean } from '@jill64/svelte-storage/serde'

const { str, num, bool } = storage({
    str: string,
    num: number,
    bool: boolean
})

// リアクティブではない
consol.log(str)
consol.log(num)
consol.log(bool)

必要ならば変数名を短くして対応しましょう。
なお、同じくSessionStorageも使えます。

import { storage } from '@jill64/svelte-storage'
import { string } from '@jill64/svelte-storage/serde'

const storage = storage(
    { ['localStorage-key']: string },
    { sessionStorage: true }
)

まとめ

いかがだったでしょうか。
この記事がタイプセーフなStorage API利用の助けになれば幸いです。

また上記ライブラリ群についてバグや不明点がありましたらぜひ各リポジトリからIssueを開いてください。

Discussion