o optics v2.0.0
Docs menu
Guides Getting Started

Getting Started

Start with a Lens for required data. A lens can always read and always write its target.

import { Lens } from '@fuiste/optics'

type Person = {
  name: string
  age: number
  address: { city: string }
}

const name = Lens<Person>().prop('name')

const ada: Person = {
  name: 'Ada',
  age: 36,
  address: { city: 'London' },
}

name.get(ada)
// 'Ada'

name.set('Augusta')(ada)
// { name: 'Augusta', age: 36, address: { city: 'London' } }

name.set((value) => value.toUpperCase())(ada)
// { name: 'ADA', age: 36, address: { city: 'London' } }

Compose A Path

Use compose(outer, inner) to focus deeper.

import { Lens, compose } from '@fuiste/optics'

type Person = {
  name: string
  address: { city: string }
}

const address = Lens<Person>().prop('address')
const city = Lens<Person['address']>().prop('city')
const personCity = compose(address, city)

personCity.get({ name: 'Ada', address: { city: 'London' } })
// 'London'

Use Prisms For Optional Paths

When a path may be absent, use a Prism. Reads return undefined, and functional updates are no-ops when the focus is missing.

import { Lens, Prism, compose } from '@fuiste/optics'

type Person = {
  name: string
  address?: { city: string }
}

const address = Prism<Person>().of({
  get: (person) => person.address,
  set: (address) => (person) => ({ ...person, address }),
})

const city = Lens<{ city: string }>().prop('city')
const optionalCity = compose(address, city)

optionalCity.get({ name: 'Ada' })
// undefined

optionalCity.set((value) => value.toUpperCase())({ name: 'Ada' })
// { name: 'Ada' }

Traverse Arrays

each creates a traversal over every element in an array.

import { Lens, compose, each } from '@fuiste/optics'

type Team = { members: string[] }

const members = Lens<Team>().prop('members')
const allMembers = compose(members, each<string>())

allMembers.modify((name) => name.toUpperCase())({
  members: ['Ada', 'Grace'],
})
// { members: ['ADA', 'GRACE'] }