
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
accessor-ts
Advanced tools
A TS/JS library for doing immutable updates and querying on nested data structures in a way that is composable and powerful.
Accessor-ts is a library for doing immutable updates and querying on nested data structures in a way that is composable and powerful. This is similar to lens and traversal libraries like partial.lenses, monacle-ts, and shades. This library aims to allow easy typed composition of optics without the bewildering functional programming jargon that usually comes with them.
npm install -S accessor-ts
import { prop, index, filter, set, all, comp } from "accessor-ts";
Simple property query
prop<{ a: number }>()("a").query({ a: 1 });
While you can inline an interface for your Accessors you probably want to define your interfaces separately and then create your accessors to match.
// Sample interface
interface User {
name: string;
id: number;
cool?: boolean;
connections: number[];
}
// Sample User
const bob: User = { name: "bob", id: 1, connections: [1, 2] };
Partially applied accessors can be stored, bound to the interface's type
const userProps = prop<User>();
set an accessor to immutably modify its target
set(userProps("id"))(3)(bob); // => { name: "bob", id: 3, connections: [1, 2] }
Trying to pass an invalid key to our accessor will be caught by TypeScript
userProps("invalid"); // `Argument of type '"invalid"' is not assignable to parameter of type '"id" | "name" | "cool" | "connections"'.ts(2345)`
You can query for optional fields
userProps("cool").query(bob); // => [undefined]
Accessors are composable so that you can extract or modify nested data structures.
interface Friend {
user: User;
}
const friendProps = prop<Friend>();
const myFriendBob: Friend = { user: bob };
comp(friendProps("user"), userProps("id")).query(myFriendBob); // => [1]
This is the same as myFriendBob.user.id.
The composed accessors are accessors themselves and can be stored and reused
const friendName = comp(friendProps("user"), userProps("name"));
set(friendName)("Robert")(myFriendBob); // => {user: {name: "Robert", id: 1, connections: [1, 2]}}
We can use Accessor.mod to run a function on the targeted value
comp(userProp("id")).mod(a => a + 1)(bob); // => { name: "bob", id: 2, connections: [1, 2] }
index can be used to focus a specific element of an array
comp(friendProps("user"), userProps("connections"), index(1)).query(
myFriendBob
); // => [1]
all() can be used to target all items within a nested array
comp(friendProps("user"), userProps("connections"), all()).query(myFriendBob); // => [1, 2]
all gets much more interesting when we have Arrays within Arrays
interface Friends {
friends: Friend[];
}
const shari: User = { name: "Shari", id: 0, connections: [3, 4] };
const myFriendShari: Friend = { user: shari };
const baz: Friends = { friends: [myFriendBob, myFriendShari] };
const makeAllFriendsCool = set(
comp(
prop<Friends>()("friends"),
all(),
friendProps("user"),
userProps("cool")
)
)(true);
makeAllFriendsCool(baz); // => Sets "cool" to true for all the users within
filter can be used to reduce the scope of an accessor to items which pass a test function. This doesn't remove items from the data structure but just changes what you get from queries or modify.
const isOdd = (a: number): boolean => a % 2 === 1;
// accessor chain as reusable value
const oddConnectionsOfFriends = comp(
prop<Friends>()("friends"),
all(),
friendProps("user"),
userProps("connections"),
filter(isOdd)
);
oddConnectionsOfFriends.query(baz) // => [1, 3]
set(oddConnectionsOfFriends)(NaN)(baz)); /* =>
{friends: [
{user: {name: "bob", id: 1, connections: [NaN, 2]}},
{user: {name: "Shari", id: 0, connections: [NaN, 4]}}
]} */
Accessors are the core of this library and have the interface:
// S is the type of the data structure that will be operated on, A is the type of some value(s) within
export interface Accessor<S, A> {
// get an array of result(s) from the data structure
query(struct: S): A[];
// modify item(s) within the data structure using the passed function
mod(fn: (x: A) => A): (struct: S) => S;
}
Since accessor-ts only provides Accessors for arrays and objects you may want to create your own if you use other data structures like Set, Map or immutable.js
: <Obj>() => <K extends keyof Obj>(k: K) => Accessor<Obj, Obj[K]>;
Create Accessor that points to a property of an object
Example:
prop<Person>()('name').query(bob) // => ['bob']
: <A>(i: number) => Accessor<A[], A>;
Create Accessor that points to an index of an array
Example:
index(1).query([1, 2, 3]) // => [2]
: <S, A>(acc: Accessor<S, A>) => (x: A) => (s: S) => S
Immutably assign using an Accessor
Example:
set(prop<Person>()('name'))('Robert')(bob) // => {name: 'Robert', ...}
: <A, B, C>(acc1: Accessor<A, B>, acc2: Accessor<B, C>) => Accessor<A, C>
Compose 2 or more Accessors (overloaded up to 8)
Examples:
comp(prop<Person>()('address'), prop<Address>()('city')).query(bob) // => ['Seattle']
: <A>() => Accessor<A[], A>
Create Accessor focused on all items in an array. query unwraps them, mod changes each item.
Examples:
const makeAllFriendsCool = (user: Person) => set(comp(prop<Person>()('friends'), all<Person>(), prop<Person>()('isCool'))(true).query(user)
// BTW you can make functions point-free if you like:
const getFriends = comp(prop<Person>()('friends'), all<Person>()).query
// is the same as
const getFriends = (user: Person) => comp(prop<Person>()('friends'), all<Person>()).query(user)
: <A>(pred: (x: A) => boolean) => Accessor<A[], A>
Create Accessor that targets items in an array that match the passed predicate. query returns the matched items, mod modifies matched items.
Example:
const getCoolFriends = (user: Person) => comp(prop<Person>()('friends'), filter<Person>(friend => friend.isCool)).query(user);
: <A>(i: number) => Accessor<A[], A>
Create Accessor that targets items in an array before the passed index
Example:
const getFirstTenFriends = comp(prop<Person>()('friends'), before(10)).query
: <A>(i: number) => Accessor<A[], A>
Create Accessor that targets items in an array after the passed index
Example:
const getMoreFriends = comp(prop<Person>()('friends'), after(9)).query
<SSub, S extends SSub = never>(keys: Array<keyof SSub>) => Accessor<S, SSub>
Create an accessor that targets a subset of properties of an object.
Example:
interface Entity {
name: string;
id: number;
}
const entityAcc = sub<Entity, User>(['name', 'id'])
entityAcc.query(bob) // => [{name: 'bob', id: 1}]
: <A>(): Accessor<A, A>
No-op Accessor
Makes Accessors a monoid in conjunction with comp. You'll probably only need this if you're writing really abstract code.
Example:
comp(prop<Person>()('name'), unit<String>()).query(bob) // => ['bob']
<A, B, C>(f: (x: A) => B, g: (y: B) => C) => (x: A) => C
Compose two functions left to right.
<A>(a: A) => (_b: unknown) => A
Constant combinator. Returns a function that ignores its argument and returns the original one.
<A>() => A[]
Returns an empty array.
<T, U>(f: (x: T) => U[]) => (xs: T[]) => U[]
Apply an array returning function to each item in an array and return an unnested array.
(index: number) => <T>(xs: T[]) => T[]
Removes item at passed index from array.
<A>(x: A) => (xs: A[]): A[]
Prepend an item to an array.
<A>(x: A) => (xs: A[]): A[]
Append an item to the end of an array
<A>(xs: A[]) => A | undefined
Return the first item in an array
<A>(xs: A[]) => A[]
Return a copy of the array excluding the first item.
(a: boolean) => boolean
Logically negate the argument.
<State>(part: Partial<State>) => (s: State) => State
Merge a partial object into the full one. Useful for updating a subset of properties of an object.
query returns an array of results, users must be careful about the array being empty.FAQs
A TS/JS library for doing immutable updates and querying on nested data structures in a way that is composable and powerful.
We found that accessor-ts demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.