Skip to content

Instantly share code, notes, and snippets.

@bfollington
Created August 20, 2019 09:04
Show Gist options
  • Save bfollington/9418220d90f0c798653d74a96a3173b6 to your computer and use it in GitHub Desktop.
Save bfollington/9418220d90f0c798653d74a96a3173b6 to your computer and use it in GitHub Desktop.
A much more complicated, generic, typesafe lens implementation for Typescript
function lens<K extends string>(key: K) {
return <const>{
get: function<R extends any>(record: R): K extends keyof R ? R[K] : unknown {
const k: string = key
return record[k]
},
set: function<R extends any>(record: R, value: R[K]) {
return record.hasOwnProperty(key) ?
{
...record,
[key]: value
} as const
:
record
}
}
}
type Lens = ReturnType<typeof lens>
const nameLens = lens('name')
const fakeLens = lens('fake')
const data = {
name: 'Bob',
age: 23
}
const x: string = nameLens.get(data)
// Error on getting property that doesn't exist
const y: string = fakeLens.get(data)
const z = nameLens.set(data, 'Roberto')
// Error on incorrect value type
const zz = nameLens.set(data, 123)
function compose(a: Lens, b: Lens) {
return <const>{
get: function<R extends any>(record: R) {
return b.get(a.get(record))
}
}
}
const nested = {
payload: {
id: '123'
}
}
const payloadLens = lens('payload')
const idLens = lens('id')
const nestedLens = compose(payloadLens, idLens)
// Annoyingly, id has lost its type here...
const id = nestedLens.get(nested)
console.log(id)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment