Skip to content

Instantly share code, notes, and snippets.

@bfollington
Last active August 20, 2019 10:02
Show Gist options
  • Save bfollington/fe176069d4ca78b7c6b95f73a2aefc7a to your computer and use it in GitHub Desktop.
Save bfollington/fe176069d4ca78b7c6b95f73a2aefc7a to your computer and use it in GitHub Desktop.
A moderately generic, moderately concise + typesafe lens implementation...
type Lens<R, K extends keyof R, O> = {
get(action: R):O,
set(action: R, value:O): R
}
function lens<R, K extends keyof R, O>(field: K): Lens<R, K, O> {
return {
get(action: R): O {
return (action[field] as unknown) as O
},
set(action: R, value: O): R {
return {
...action,
[field]: value,
}
},
}
}
function compose<T1 extends { [x: string]: any }, K1 extends keyof T1, O1, T2 extends T1[K1], K2 extends keyof T2, O2>(a: Lens<T1, K1, O1>, b: Lens<T2, K2, O2>): Lens<T1, K1, O2> {
return {
get: (r: T1) => b.get(a.get(r) as T2),
set: (r: T1, v: any) => a.set(r, b.set(a.get(r) as T2, v))
}
}
interface INamed {
name: string
}
const Name = lens<INamed, 'name', string>('name')
interface IAction<T> {
payload: T
}
const Action = lens<IAction<INamed>, 'payload', INamed>('payload')
interface IWtf {
action: IAction<INamed>
}
const Wtf = lens<IWtf, 'action', IAction<INamed>>('action')
const data = {
payload: {
name: 'Roberto'
}
}
const nested = compose(Action, Name)
// Our composition doesn't recurse, hm...
compose(Wtf, nested)
console.log(nested.get(data))
console.log(nested.set(data, 'Jonny'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment