Last active
August 20, 2019 10:02
-
-
Save bfollington/fe176069d4ca78b7c6b95f73a2aefc7a to your computer and use it in GitHub Desktop.
A moderately generic, moderately concise + typesafe lens implementation...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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