A mock of localStorage written in TypeScript
Tags: localstorage, typescript, mock
Published: 2021-11-05 19:20:51

## A mock of localStorage written in TypeScript

The other day, I started writing mock of localStorage in TypeScript.
Here is the code that I actually wrote.

Regarding this code, please note that it does not behave completely the same as localStorage.

**The behavior is not perfectly reproduced (The value returned when an unintended value is passed, for example), but the basic logic is implemented.**

```typescript
type Store = any;

class LocalStorageMock {
  store: Store;
  length: number;

  constructor() {
    this.store = {};
    this.length = 0;
  }

  key(n: number): any {
    if (typeof n === 'undefined') {
      throw new Error(
        "Uncaught TypeError: Failed to execute 'key' on 'Storage': 1 argument required, but only 0 present."
      );
    }

    if (n >= Object.keys(this.store).length) {
      return null;
    }

    return Object.keys(this.store)[n];
  }

  getItem(key: string): Store | null {
    if (!Object.keys(this.store).includes(key)) {
      return null;
    }

    return this.store[key];
  }

  setItem(key: string, value: any): undefined {
    if (typeof key === 'undefined' && typeof value === 'undefined') {
      throw new Error(
        "Uncaught TypeError: Failed to execute 'setItem' on 'Storage': 2 arguments required, but only 0 present."
      );
    }

    if (typeof value === 'undefined') {
      throw new Error(
        "Uncaught TypeError: Failed to execute 'setItem' on 'Storage': 2 arguments required, but only 1 present."
      );
    }

    if (!key) return undefined;

    this.store[key] = value.toString() || '';
    this.length = Object.keys(this.store).length;

    return undefined;
  }

  removeItem(key: string): undefined {
    if (typeof key === 'undefined') {
      throw new Error(
        "Uncaught TypeError: Failed to execute 'removeItem' on 'Storage': 1 argument required, but only 0 present."
      );
    }

    delete this.store[key];
    this.length = Object.keys(this.store).length;
    return undefined;
  }

  clear(): undefined {
    this.store = {};
    this.length = 0;

    return undefined;
  }
}

export const getLocalStorageMock = (): any => {
  return new LocalStorageMock();
};
```

## About localStorage behavior

I actually investigated the behavior of localStorage in the browser before writing this code.
I noticed that it behaved in a slightly interesting way.

For example, the following code.
(I didn't do an exhaustive check, so there may be other unique behaviors hidden in the code)

Note that the environment has been tested with Chrome `94.0.4606.81 (Official Build) (arm64)`.
I haven't tried it with other browsers, so the behavior might be different with different browsers.

```javascript
window.localStorage.setItem('test', 'aaa')

window.localStorage.key(0)
// => 'test'

window.localStorage.key('a')
// => 'test'

window.localStorage.key(false)
// => 'test'

window.localStorage.key(undefined)
// => 'test'

window.localStorage.key(null)
// => 'test'
```

Note that if you do not pass any argument to the key function, you will get an error.

```javascript
window.localStorage.key()
// => Uncaught TypeError: Failed to execute 'key' on 'Storage': 1 argument required, but only 0 present.
```

Here is the MDN documentation on the key function.
https://developer.mozilla.org/ja/docs/Web/API/Storage/key

## A mock of localStorage is available on npm

The localStorage mock I wrote at the beginning of this post, written in TypeScript, is available on npm.


[@shinshin86/local-storage-mock](https://www.npmjs.com/package/@shinshin86/local-storage-mock)

[localStorage Mock(GitHub)](https://github.com/shinshin86/local-storage-mock)

Actually, it is helping me write tests for localStorage in another project of mine.
If anyone is considering using it for a similar purpose, please give it a try.


Installation is done like this.

```sh
npm install @shinshin86/local-storage-mock
# or
yarn add @shinshin86/local-storage-mock
```

It can be made to work in this way.

```javascript
const { getLocalStorageMock } = require('local-storage-mock');

const window = {
  localStorage: getLocalStorageMock(),
};

window.localStorage.setItem('testkey', 'testvalue');
console.log(window.localStorage.getItem('testkey'));
// => testvalue

console.log(window.localStorage.key(0));
// => testkey

console.log(window.localStorage.length);
// => 1

window.localStorage.removeItem('testkey');
console.log(window.localStorage.getItem('testkey'));
// => null

window.localStorage.setItem('testkey', 'testvalue');
console.log(window.localStorage.getItem('testkey'));
// => testvalue

window.localStorage.clear();
console.log(window.localStorage.length);
// => 0

console.log(window.localStorage.getItem('testkey'));
// => null
```

Thanks for reading to the end!