Skip to content

Instantly share code, notes, and snippets.

@sonhanguyen
Last active November 30, 2020 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sonhanguyen/c5f5883de68c2f0134f78c1f26051ff1 to your computer and use it in GitHub Desktop.
Save sonhanguyen/c5f5883de68c2f0134f78c1f26051ff1 to your computer and use it in GitHub Desktop.
typescript optics
type Lens<State, Value> = {
(_: State): Value
set: (_: Value) => (_: State) => State
map<T>(_: Lens<Value, T>): Lens<State, T>
}
type Prop<K extends string, V = any> = Lens<Record<K, V>, V>
const prop = <K extends string, V>(key: K, defaultVal?: V): Prop<K, V> => {
const get = (obj: any) => obj[key] || defaultVal
const set = (val: any) => (obj: any) => ({ ...obj, [key]: val })
return lens(get, set) as any
}
const lens = <S, T>(
get: (obj: S) => T,
set: (val: T) => (_: S) => S
): Lens<S, T> => {
const lens1 = (obj: S) => get(obj)
return Object.assign(lens1, { set, map:
<V extends any>(lens2: Lens<T, V>) => composeLenses(lens1 as any, lens2)
})
}
const lensMap = <P extends string[]>(...props: P): {
[K in P[number]]: Prop<K>
} => Array<any>({}, ...props).reduce(
(map, key) => ({ ...map, [key]: prop(key, {}) })
)
const composeLenses = <X, Y, Z>(lens1: Lens<X, Y>, lens2: Lens<Y, Z>): Lens<X, Z> => {
const get = (obj: X) => lens2(lens1(obj))
const set = (val: Z) => (obj: X) => lens1.set(lens2.set(val)(lens1(obj)))(obj)
return lens(get, set) as any
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment