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 🙏

© 2025 – Pkg Stats / Ryan Hefner

selectorator

v5.0.0

Published

Simplified generator of reselect selectors

Readme

selectorator

selectorator is an abstraction API for creating selectors via reselect with less boilerplate code.

Usage

import { createSelector } from 'selectorator';

interface State {
  foo: {
    bar: string;
  };
  baz: string;
}

const getBarBaz = createSelector<State>()(['foo.bar', 'baz'], (bar, baz) => `${bar} ${baz}`);

const state = {
  foo: { bar: 'bar' },
  baz: 'baz',
};

console.log(getBarBaz(state)); // "bar baz"

Not a whole lot of magic here, just simplifying the creation of the "identity selectors" that reselect requires, instead replacing them with a convenient and flexible syntax for retrieval of a nested property in the state object. See selection types for more details. It should be noted the list of input selectors must be wrapped in an array:

// not allowed
const getThing = createSelector<State>()('thing');
// works
const getThing = createSelector<State>()(['thing']);

This is because it allows for narrow typing to flow throughout. This is also the reason for the curried approach, which you can get more details here. But because of that curried structure, it is a common practice to create an app- or state-specific selector:

import { createSelector } from 'selectorator';
import type { AppState } from './types';

export const createAppSelector = createSelector<AppState>();

This avoids the boilerplate of passing the type and currying all uses across the app. Here is the example from the reselect README modified to use selectorator which uses this to compose selectors:

import { createAppSelector } from './utils';

// subtotal built using simple method
const getSubtotal = createAppSelector(['shop.items'], (items) => items.reduce((sum, { value }) => sum + value, 0));

// tax built with simple method combined with other selector
const getTax = createAppSelector(
  [getSubtotal, 'shop.taxPercent'],
  (subtotal, taxPercent) => subtotal * (taxPercent / 100),
);

// total built entirely with other selectors
const getTotal = createAppSelector([getSubtotal, getTax], (subtotal, tax) => subtotal + tax);

// example state
const state = {
  shop: {
    taxPercent: 8,
    items: [
      { name: 'apple', value: 1.2 },
      { name: 'orange', value: 0.95 },
    ],
  },
};

console.log('subtotal: ', getSubtotal(state)); // 2.15
console.log('tax: ', getTax(state)); // 0.172
console.log('total: ', getTotal(state)); // {2.322}

Selection types

For compatibility and compisition, passing a manual selector like in the upstream reselect library is supported.

const getThing = createSelector<State>()([(state) => state.thing]);

In addition, selectorator uses pathington under-the-hood for path parsing, and that can be leveraged to retrieve nested values:

  • Pulls from state (the first argument passed):
    • string => 'foo[0].bar' (pulls from state.foo[0].bar)
    • number => 0 (pulls from state[0])
    • Array => ['foo', 0, 'bar'] (pulls from state.foo[0].bar)

If passing multiple parameters (e.g., selector(state, props)), you can leverage the object option wrapper to specify which argument to select from.

const getThings = createSelector<State>()({
  stateThing: 'thing',
  propsThing: { argIndex: 1, path: 'thing' },
});

Both manual and pathington path selection types are supported in object form.

Default selectors

To help minimize boilerplate, selectorator has default computation selectors for common use-cases.

Single item

For a selector that only retrieves a single state value, it returns that value.

interface {
  nested: {
    thing: string;
  }
}

const getThing = createSelector<State>()(['nested.thing']);
// `getThing` has signature `(state: State) => string`

Multiple items

If you want to retrieve multiple items, then you can use a structured selector.

interface {
  nested: {
    thing: string;
    otherThing: number;
  }
}

const getThing = createSelector<State>()({
  thing: 'nested.thing',
  otherThing: ['nested', 'otherThing'],
});
// `getThing` has signature `(state: State) => { thing: string; otherThing: string }`

The reason a structured selector is used is because this automatically ascribes a name to the selected values, which is a much more natural consumption mechnanism than destructuring an array of results. It also allows for smaller diffs if selections are added / removed.

TypeScript

To support simple and fluid typing, a currying syntax is used to allow narrow typing for input selectors, as well as the computation selector. When creating a selector that accepts multiple params, the state should be array of the input types, e.g. createSelector<[State, number[], boolean]>(options).

import { createSelector } from 'selectorator';

interface State {
  foo: {
    bar: string;
  };
  baz: string;
}

interface Props {
  quz: number[];
}

// `State` is input type
const getBarBaz = createSelector<State>()([({ foo }) => foo, 'baz'], (foo, baz) => `${foo.bar} ${baz}`);
// `(state) => state.foo` has type signature `(state: State) => string`
// `(foo, baz) => `${foo.bar} ${baz}` has type signature `(foo: { bar: string }, baz: string) => string`
// `getBarBaz() `has type signature: `(state: State) => string`;

// State and `Props` are type  input types
const getBarBaz = createSelector<[State, Props]>(
  [({ foo }) => foo, 'baz', 'baz', { path: ['quz', 0], argIndex: 1 }],
  (foo, baz, quz) => [`${foo.bar} ${baz}`, quz] as const,
);
// `(state) => state.foo` has type signature `(state: State) => string`
// `(foo, baz, quz) => `[`${foo.bar} ${baz}`, quz] as const` has type signature
//    `(foo: { bar: string }, baz: string, quz: number) => readonly [string, number]`
// `getBarBaz() `has type signature: `(state: State, props: Props) => readonly [string, number]`;

If you pass a very wide type, or no type at all, any is used for all values.

// `any` is input type
const getBarBaz = createSelector<any>()([({ foo }) => foo, 'baz'], (foo, baz) => `${foo.bar} ${baz}`);
// `(state) => state.foo` has type signature `(state: any) => string`
// `(foo, baz) => `${foo.bar} ${baz}` has type signature `(foo: any, baz: string) => string`
// `getBarBaz() `has type signature: `(state: any) => string`;

// `unknown` is input type (either inferred when not passed, or can be explicitly passed)
const getBarBaz = createSelector()([({ foo }) => foo, 'baz'], (foo, baz) => `${foo.bar} ${baz}`);
// `(state) => state.foo` has type signature `(state: any) => string`
// `(foo, baz) => `${foo.bar} ${baz}` has type signature `(foo: any, baz: string) => string`
// `getBarBaz() `has type signature: `(state: any) => string`;

The narrow typing is also available for structured selectors!

const getStucturedBarBaz = createSelector<[State, Props]>(
  {
    foo: ({ foo }) => foo,
    baz: 'baz',
    quz: { argIndex: 1, path: ['quz', 0] },
  },
  (foo, baz, quz) => [`${foo.bar} ${baz}`, quz] as const,
);

// getStructuredBarBaz() has type signature: (foo: { bar: string }, baz: string, quz: number) => readonly [string, number];

Options

If desired for customization, you can pass through the createSelectorCreator options in reselect when creating the selector.

import { deepEqual } from 'fast-equals';
import { createSelector } from 'selectorator';
import type { AppState } from './types';

export const createAppSelector = createSelector<AppState>({
  argsMemoizeOptoins: { resultEqualityCheck: deepEqual },
  memoizeOptions: { resultEqualityCheck: deepEqual },
});