Gathering detailed insights and metrics for monocle-ts
Gathering detailed insights and metrics for monocle-ts
Gathering detailed insights and metrics for monocle-ts
Gathering detailed insights and metrics for monocle-ts
io-ts-types
A collection of codecs and combinators for use with io-ts
@effect-ts/monocle
This package is forked from `https://github.com/gcanti/monocle-ts` and adapted to work with the `@effect-ts` ecosystem. All credits the to original authors.
effect-monocle
Port of some monocle-ts features to effect-ts
spectacles-ts
Practical Optics • Unfancy monocle-ts 🧐
Functional optics: a (partial) porting of Scala monocle
npm install monocle-ts
Typescript
Module System
Node Version
NPM Version
TypeScript (99.8%)
JavaScript (0.2%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
1,066 Stars
271 Commits
54 Forks
22 Watchers
2 Branches
16 Contributors
Updated on Jun 09, 2025
Latest Version
2.3.13
Package Id
monocle-ts@2.3.13
Unpacked Size
359.71 kB
Size
36.74 kB
File Count
103
NPM Version
6.10.0
Node Version
12.7.0
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
(Adapted from monocle site)
Modifying immutable nested object in JavaScript is verbose which makes code difficult to understand and reason about.
Let's have a look at some examples:
1interface Street { 2 num: number 3 name: string 4} 5interface Address { 6 city: string 7 street: Street 8} 9interface Company { 10 name: string 11 address: Address 12} 13interface Employee { 14 name: string 15 company: Company 16}
Let’s say we have an employee and we need to upper case the first character of his company street name. Here is how we could write it in vanilla JavaScript
1const employee: Employee = { 2 name: 'john', 3 company: { 4 name: 'awesome inc', 5 address: { 6 city: 'london', 7 street: { 8 num: 23, 9 name: 'high street' 10 } 11 } 12 } 13} 14 15const capitalize = (s: string): string => s.substring(0, 1).toUpperCase() + s.substring(1) 16 17const employeeCapitalized = { 18 ...employee, 19 company: { 20 ...employee.company, 21 address: { 22 ...employee.company.address, 23 street: { 24 ...employee.company.address.street, 25 name: capitalize(employee.company.address.street.name) 26 } 27 } 28 } 29}
As we can see copy is not convenient to update nested objects because we need to repeat ourselves. Let's see what could
we do with monocle-ts
1import { Lens } from 'monocle-ts' 2 3const company = Lens.fromProp<Employee>()('company') 4const address = Lens.fromProp<Company>()('address') 5const street = Lens.fromProp<Address>()('street') 6const name = Lens.fromProp<Street>()('name')
compose
takes two Lenses
, one from A
to B
and another one from B
to C
and creates a third Lens
from A
to
C
. Therefore, after composing company
, address
, street
and name
, we obtain a Lens
from Employee
to
string
(the street name). Now we can use this Lens
issued from the composition to modify the street name using the
function capitalize
1const capitalizeName = company.compose(address).compose(street).compose(name).modify(capitalize) 2 3assert.deepStrictEqual(capitalizeName(employee), employeeCapitalized)
You can use the fromPath
API to avoid some boilerplate
1import { Lens } from 'monocle-ts' 2 3const name = Lens.fromPath<Employee>()(['company', 'address', 'street', 'name']) 4 5const capitalizeName = name.modify(capitalize) 6 7assert.deepStrictEqual(capitalizeName(employee), employeeCapitalized) // true
Here modify
lift a function string => string
to a function Employee => Employee
. It works but it would be clearer
if we could zoom into the first character of a string
with a Lens
. However, we cannot write such a Lens
because
Lenses
require the field they are directed at to be mandatory. In our case the first character of a string
is
optional as a string
can be empty. So we need another abstraction that would be a sort of partial Lens, in
monocle-ts
it is called an Optional
.
1import { Optional } from 'monocle-ts' 2import { some, none } from 'fp-ts/Option' 3 4const firstLetterOptional = new Optional<string, string>( 5 (s) => (s.length > 0 ? some(s[0]) : none), 6 (a) => (s) => (s.length > 0 ? a + s.substring(1) : s) 7) 8 9const firstLetter = company.compose(address).compose(street).compose(name).asOptional().compose(firstLetterOptional) 10 11assert.deepStrictEqual(firstLetter.modify((s) => s.toUpperCase())(employee), employeeCapitalized)
Similarly to compose
for lenses, compose
for optionals takes two Optionals
, one from A
to B
and another from
B
to C
and creates a third Optional
from A
to C
. All Lenses
can be seen as Optionals
where the optional
element to zoom into is always present, hence composing an Optional
and a Lens
always produces an Optional
.
The stable version is tested against TypeScript 3.5.2, but should run with TypeScript 2.8.0+ too
monocle-ts version | required typescript version |
---|---|
2.0.x+ | 3.5+ |
1.x+ | 2.8.0+ |
Note. If you are running < typescript@3.0.1
you have to polyfill unknown
.
You can use unknown-ts as a polyfill.
2.3+
)Experimental modules (*) are published in order to get early feedback from the community.
The experimental modules are independent and backward-incompatible with stable ones.
(*) A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice.
From monocle@2.3+
you can use the following experimental modules:
Iso
Lens
Prism
Optional
Traversal
At
Ix
which implement the same features contained in index.ts
but are pipe
-based instead of class
-based.
Here's the same examples with the new API
1interface Street { 2 num: number 3 name: string 4} 5interface Address { 6 city: string 7 street: Street 8} 9interface Company { 10 name: string 11 address: Address 12} 13interface Employee { 14 name: string 15 company: Company 16} 17 18const employee: Employee = { 19 name: 'john', 20 company: { 21 name: 'awesome inc', 22 address: { 23 city: 'london', 24 street: { 25 num: 23, 26 name: 'high street' 27 } 28 } 29 } 30} 31 32const capitalize = (s: string): string => s.substring(0, 1).toUpperCase() + s.substring(1) 33 34const employeeCapitalized = { 35 ...employee, 36 company: { 37 ...employee.company, 38 address: { 39 ...employee.company.address, 40 street: { 41 ...employee.company.address.street, 42 name: capitalize(employee.company.address.street.name) 43 } 44 } 45 } 46} 47 48import * as assert from 'assert' 49import * as L from 'monocle-ts/Lens' 50import { pipe } from 'fp-ts/function' 51 52const capitalizeName = pipe( 53 L.id<Employee>(), 54 L.prop('company'), 55 L.prop('address'), 56 L.prop('street'), 57 L.prop('name'), 58 L.modify(capitalize) 59) 60 61assert.deepStrictEqual(capitalizeName(employee), employeeCapitalized) 62 63import * as O from 'monocle-ts/Optional' 64import { some, none } from 'fp-ts/Option' 65 66const firstLetterOptional: O.Optional<string, string> = { 67 getOption: (s) => (s.length > 0 ? some(s[0]) : none), 68 set: (a) => (s) => (s.length > 0 ? a + s.substring(1) : s) 69} 70 71const firstLetter = pipe( 72 L.id<Employee>(), 73 L.prop('company'), 74 L.prop('address'), 75 L.prop('street'), 76 L.prop('name'), 77 L.composeOptional(firstLetterOptional) 78) 79 80assert.deepStrictEqual( 81 pipe( 82 firstLetter, 83 O.modify((s) => s.toUpperCase()) 84 )(employee), 85 employeeCapitalized 86)
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 3/14 approved changesets -- score normalized to 2
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
40 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More