freddi301
5/14/2018 - 9:21 AM

Lens in Typescript


export interface Lens<T, V> {
    get(o: T): V;
    set(v: V, o: T): T;
}

export const identity = <T>(): Lens<T, T> => identityLens

const identityLens = ({
    get: (o) => o,
    set: (v, o) => v
})

export const property = <T, K extends keyof T>(k: K): Lens<T, T[K]> => ({
    get: (o: T): T[K] => o[k],
    set: (v: T[K], o: T): T => ({ ...o as any, [k]: v })
})

export const properties = <T>(): { [K in keyof T]: Lens<T, T[K]> } => propertiesProxy

const propertiesProxy = new Proxy(null, {
    get(o, k) { return property(k as string); }
});

export const compose = <A, B, C>(x: Lens<A, B>, y: Lens<B, C>): Lens<A, C> => ({
  get: (a: A) => y.get(x.get(a)),
  set: (c: C, a: A) => {
    const b: B = x.get(a);
    const b2: B = y.set(c, b);
    return x.set(b2, a);
  }
});

type inner = { x: number };
const x = property<inner, "x">("x");

type outer = { y: inner };
const y = property<outer, "y">("y");

const u = x.get({x:4})

const u2 = compose(y, x);

const innerLens = properties<inner>()

const u3 = compose(properties<outer>().y, properties<inner>().x)