Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dvdotsenko/33c5b6ba6a88584255069894ac5c9430 to your computer and use it in GitHub Desktop.
Save dvdotsenko/33c5b6ba6a88584255069894ac5c9430 to your computer and use it in GitHub Desktop.
TypeScript static initializer generic collection serialize / deserialize example with localStorage
export interface Serializable {
id: string
}
export interface SerializableStatic<T> {
new (data: Record<string, any>): T
}
export interface CollectionBase<T> {
add: (o: T) => void
get: (id: string) => T | null
values: () => Generator<T>
}
export class MemoryCollection<T> implements CollectionBase<T>{
collection: Map<string, T>
constructor() {
this.collection = new Map<string, T>()
}
add(o: T) {
const id = (o as Serializable).id
this.collection.set(id, o)
}
get(id: string) {
return this.collection.get(id) || null
}
* values() {
let key: string
let value: T
for ([key, value] of this.collection) {
yield value
}
}
}
export class LocalStoreCollection<Tstat extends SerializableStatic<T>, T extends Serializable = InstanceType<Tstat>> implements CollectionBase<T>{
namespace: string
instantiator: Tstat
constructor(instantiator: Tstat, namespace: string) {
this.namespace = namespace + '.'
this.instantiator = instantiator
}
add(o: T) {
const id = (o as Serializable).id
const key = this.namespace + id
const serialized = JSON.stringify(o)
localStorage.setItem(key, serialized)
}
_deserialize(dataStr: string): T {
const data: Record<string, unknown> = JSON.parse(dataStr);
return new this.instantiator(data)
}
get(id: string): T | null {
const key = this.namespace + id
const v = localStorage.getItem(key)
if (v) {
return this._deserialize(v)
} else {
return null
}
}
* values() {
let key: string | null
let i: number
let v_str: string | null
for (i = localStorage.length - 1; i >= 0; i--) {
key = localStorage.key(i)
if (key) {
if (key.startsWith(this.namespace)) {
v_str = localStorage.getItem(key)
if (v_str) {
yield this._deserialize(v_str)
}
}
}
}
}
}
export class MemoryCachedLocalStoreCollection<Tstat extends SerializableStatic<T>, T extends Serializable = InstanceType<Tstat>> implements CollectionBase<T>{
memoryCollection: MemoryCollection<T>
localStoreCollection: LocalStoreCollection<Tstat, T>
constructor(instantiator: Tstat, namespace: string) {
this.localStoreCollection = new LocalStoreCollection(instantiator, namespace)
this.memoryCollection = new MemoryCollection<T>()
}
add(o: T) {
this.memoryCollection.add(o)
this.localStoreCollection.add(o)
}
get(id: string) {
let v = this.memoryCollection.get(id)
if (v) {
return v
}
v = this.localStoreCollection.get(id)
if (v) {
this.memoryCollection.add(v)
}
return v
}
* values() {
for (let v of this.localStoreCollection.values()) {
this.memoryCollection.add(v)
yield v
}
}
}
/////////////// usage //////////////
interface ThingLike {
id?: string
name?: string
}
export class Thing implements Serializable {
id: string
name: string
constructor(o:ThingLike) {
this.id = o.id ? o.id : crypto.randomUUID()
this.name = o.name ? o.name : ''
}
}
const cc = new LocalStoreCollection(Thing, 'things')
const thing = new Thing({name:'Thing One'})
cc.add(thing)
const thing_again: Thing | null = cc.get(thing.id)
@dvdotsenko
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment