diff --git a/README.md b/README.md index 7e4c187..49d42a0 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,21 @@ myLocalStorage.bar.spam = 'This works too!'; const copied = { ...myLocalStorage }; ``` -In TypeScript, you can pass an interface as a [generic type parameter](https://www.typescriptlang.org/docs/handbook/generics.html) to the factory function: +Additionally, you can pass default values. This is handy if your stored data contains deep objects that need to be accessible even when the contained data is `undefined`: + +```ts +const myLocalStorage = StorageProxy.createLocalStorage('my-namespace', { + one: { + two: 'three', + four: {}, + }, +}); + +console.log(myLocalStorage.one.two.three) // => "three" +myLocalStorage.one.two.three.four.five = 'six'; // Works! +``` + +In TypeScript, you can define the shape of your stored data by passing a [generic type parameter](https://www.typescriptlang.org/docs/handbook/generics.html) to the factory function: ```ts const myStorage = StorageProxy.createLocalStorage<{ @@ -49,4 +63,8 @@ const myStorage = StorageProxy.createLocalStorage<{ foo: number[]; bar: { baz: string, spam?: string }; }>('my-namespace'); + +myStorage.foo // Works! +myStorage.bar.baz // Works! +myStorage.yolo // Compiler error! ``` diff --git a/package.json b/package.json index 6539e48..757f90b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "storage-proxy", - "version": "0.11.1", + "version": "0.11.2", "description": "Use web storage (localStorage/sessionStorage) just like plain objects using ES6 Proxies.", "author": "Ian K Smith ", "license": "MIT", diff --git a/src/lib.ts b/src/lib.ts index 2626008..a40ece4 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -24,6 +24,17 @@ export type StorageProxy = Partial & { // --- Utilities ------------------------------------------------------------ // +/** + * Checks if the passed value is undefined. + * + * @param value - The value to check. + * + * @return Returns true if value is undefined, else false. + */ +export function isUndefined(value: any): value is undefined { + return value === undefined; +} + /** * Initializes the web storage interface. If no storage exists, we save an empty * object. @@ -73,13 +84,13 @@ function createProxy( [isStorageProxy]: true, [storageTargetSymbol]: storageTarget, }; - const proxyData = onChange(data, (path, value, prevValue) => { + const proxyData = onChange(data, (_path, value, prevValue) => { if (value === prevValue) return; window[storageTarget].setItem(namespace, JSON.stringify(proxyData)); }); const storageProxy = new Proxy(proxyData, { - get: (target, prop, receiver) => { + get: (_target, prop, _receiver) => { if (typeof proxyData[prop as any] === 'undefined') return null; return proxyData[prop as any]; }, @@ -87,7 +98,7 @@ function createProxy( if (defaults) { for (const [key, value] of Object.entries(defaults)) { - if (!data[key]) { + if (isUndefined(data[key])) { storageProxy[key] = value; } } @@ -106,7 +117,8 @@ export const StorageProxy = { /** * Creates a `localStorage` proxy object that can be used like a plain JS object. * - * @param namespace - An optional namespace to prefix `localStorage` keys with. + * @param namespace - A namespace to prefix `localStorage` keys with. + * @param defaults - Optional default values for this `StorageProxy` object. * * @return a `StorageProxy` object targeting `localStorage`. */ @@ -120,7 +132,8 @@ export const StorageProxy = { /** * Creates a `sessionStorage` proxy object that can be used like a plain JS object. * - * @param namespace - An optional namespace to prefix `sessionStorage` keys with. + * @param namespace - A namespace to prefix `sessionStorage` keys with. + * @param defaults - Optional default values for this `StorageProxy` object. * * @return a `StorageProxy` object targeting `sessionStorage`. */