npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fluent-accessor

v1.2.3

Published

Create accessor functions for nested properties easily, inclemently, strongly typed and beautifully!

Downloads

19

Readme

Actions Status Actions Status Actions Status Test Coverage Maintainability Packages npm version

Dynamically creates an accessor function based on property paths!

How to use it

Just use $ to starts the navigation:

const func = $<MyType>().field1.field2.field3[0].field4;

func will be an accessor for the field4 following the whole specified path

console.log(func(myInstance)); // returns the value of myInstance.field1.field2.field3[0].field4;

You can also run it with a fallback

console.log(func(myInstance, null)); // In case of some property not existing, it will return null;

If you want to return undefined instead, you also can, setting off the strictness of the function:

setStrictness(func, false);
console.log(func(myInstance)); // In case of some property not existing, it will return undefined;

You can aplly fallback for a func implicitly

applyFallback(field4, 123);
console.log(func(myInstance)); // In case of some property not existing, it will return 123;

You can retrieve the navigated properties

const func = jsonPath(func); // return an iterable that yields 'field1', 'field2', 'field3', '0' and, then, 'field4'

Finally, using TypeScript, let's suppose you have a function like this:

function doStuff(field: (a: MyType) => T) {
  // do some stuff
}

Typescript can infer the generic types if you call it like this:

const result = doStuff($('field1', 'field2'));

This last option, combined with TypeScript, is very powerful! Because of contextual typing, you can infer each property to navigate without informing any type! Of course, if you didn't use "any" in your parameters ;)

But why?

You my ask yourself why would you do all this work if you can simply create an expression like this directly:

const func = (x) => x.field1.field2.field3[0].field4;

The reasons are: information and IDE helping. Using fluent-accessor you can have more than just a function that returns the value of a nested field:

  • You can know the path you're accessing just by using jsonPath;
  • You have control of scope: you know that the generated expression will only return the field value;
  • You can have auto-complete while programming even though you're not dealing with the said object;
  • You can compose the expression to access the nested property dynamically;

Some possible applications

Imagine you have a object like this:

interface Nested {
  nestedFoo: MyPOCO;
  bar: string;
}

interface MyPOCO {
  foo: Nested;
  bar: string;
}

And you want to create a function that sorts an array by a nested field. Let's say the first implementation you just use a string to do it:

function mySort<T>(arr: T, fields: string);

// Usage
mySort(myPOCOArray, 'foo.nestedfoo.foo.nestedFoo.bar');

That's can work, but because the lack of IDE auto-complete and lint helping, you may let a typo pass, like in the example above. Let's try with an array:

function mySort<T>(arr: T, fields: Array<T[keyof T]>);

// Usage
mySort(myPOCOArray, ['foo', 'nestedFoo', 'foo', 'nestedFoo', 'bar']);

Well, that actually will not compile because of the type you specified for the array. You can use string, but then, you'll have the same problem of the first approach. Now, let's try with fluent-accessor

function mySort<T>(arr: T, fields: Expression<T, unknown>);

// Usage
mySort(myPOCOArray, $('foo', 'nestedFoo', 'foo', 'nestedFoo', 'bar'));

Now you have it. You have a strong typed function which will help you to not typo over your implementation.

Also, internally, you have access to all the nested field names you'll consider in your index because, I don't know, you want to register it in some place.

get and getOrDef

You can also use this library to get directly the value of a nested property, like this:

const result = get(myInstance, 'foo', 'bar', '0', 'fooBar');

The advantage of it? result is strongly typed by default! In this call, if the nested path is not found, result will be undefined. If you want to define a default value for it, use getOrDef

const result = getOrDef(myInstance, 99, 'foo', 'bar', '0', 'fooBar');

Special symbols

Let's assume that, in the example above, nestedFoo is a number. The accessor generated by $ will return the triple of it! You also have some custom built in mappers, like:

someFunc($('foo', 'nestedFoo', 'foo', 'nestedFoo', baseOp.first);
someFunc($('foo', 'nestedFoo', 'foo', 'nestedFoo', baseOp.last);

In the examples above, nestedFoo need to be an iterable, otherwise the typing will throw an error.

License

Licensed under MIT.