Skip to content

Instantly share code, notes, and snippets.

@bpander
Created September 27, 2019 14:43
Show Gist options
  • Save bpander/6dd94fe4ac5184b5566c3a0f8a3597f1 to your computer and use it in GitHub Desktop.
Save bpander/6dd94fe4ac5184b5566c3a0f8a3597f1 to your computer and use it in GitHub Desktop.
import _get from 'lodash/get';
import { setIn } from './objects';
// This is a lens utility based off of https://github.com/utatti/lens.ts with 3 main differences.
// 1. It keeps track of the path from the source to the destination
// 2. It works by feeding that path into lodash's get/setWith functions
// 3. It doesn't use proxies (so IE is supported)
export type Setter<T> = (state: T) => T;
export interface Lens<T, U = T> {
k: <K extends keyof U>(key: K) => Lens<T, U[K]>;
get: (input: T) => U;
set: (setter: Setter<U>) => Setter<T>;
pipe: <V>(otherLens: Lens<U, V>) => Lens<T, V>;
path: (string | number)[];
}
const makeLensFromPath = <T extends object, U>(path: (string | number)[]): Lens<T, U> => {
const get = (input: T) => (!path.length) ? input : _get(input, path);
return {
path,
get,
k: <K extends keyof U>(key: K) => makeLensFromPath<T, U[K]>([ ...path, key as (string | number) ]),
set: (setter: Setter<U>) => (input: T) => setIn(input, path, setter(get(input))),
pipe: <V>(lens: Lens<U, V>) => makeLensFromPath<T, V>([ ...path, ...lens.path ]),
};
};
export const makeLens = <TInput extends object>(): Lens<TInput> => makeLensFromPath([]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment