Skip to content

Instantly share code, notes, and snippets.

@freddi301
Last active December 11, 2018 14:53
Show Gist options
  • Save freddi301/44647114e52eed89e3a2fc52290558d0 to your computer and use it in GitHub Desktop.
Save freddi301/44647114e52eed89e3a2fc52290558d0 to your computer and use it in GitHub Desktop.
Lens in typescript
export const get = Symbol("get");
export const set = Symbol("set");
interface Lens<T, V> {
[get]: (o: T) => V;
[set]: (v: V) => (t: T) => T;
}
const identity = <T>(): Lens<T, T> => ({
[get]: t => t,
[set]: v => t => v
});
const property = <T, K extends keyof T>(k: K): Lens<T, T[K]> => ({
[get]: (t: T): T[K] => t[k],
[set]: (v: T[K]) => (t: T): T => ({ ...t, [k]: v })
});
type PropertiesLens<P, T> = Lens<P, T> &
{ [K in keyof T]: PropertiesLens<P, T[K]> };
export const properties = <T>(): PropertiesLens<T, T> =>
propertiesRecursive(identity());
const propertiesRecursive = <P, T>(p: Lens<P, T>): PropertiesLens<P, T> =>
new Proxy(p, {
get(o, k) {
if (k === get) return p[get];
if (k === set) return p[set];
else if (typeof k === "string")
return propertiesRecursive(
compose(
p,
property(k as keyof T)
)
);
else return (o as any)[k];
}
}) as PropertiesLens<P, T>;
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);
}
});
// well typed examples
type inner = { x: number };
const innerX = property<inner, "x">("x");
type outer = { y: inner };
const outerY = property<outer, "y">("y");
const num = innerX[get]({ x: 4 });
const outerYinnerX = compose(
outerY,
innerX
);
const innerLens = properties<inner>();
const inlineCcomposition = compose(
properties<outer>().y,
properties<inner>().x
);
const outerSample = { y: { x: 4 } };
console.log([
inlineCcomposition[get](outerSample),
inlineCcomposition[set](8)(outerSample)
]);
console.log([
properties<outer>().y[get](outerSample),
properties<outer>().y.x[set](8)(outerSample)
]);
const outerYInnerX = properties<outer>().y.x;
const outerLens = properties<outer>();
console.log([
outerLens.y[get](outerSample),
outerLens.y.x[set](8)(outerSample)
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment