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

@edcarroll/class-discriminators

v2.0.0

Published

Aids Typescript type checking for class based discriminated unions

Downloads

7

Readme

Class Discriminators

Aids Typescript type checking for class based discriminated unions.

Note that the object being tested doesn't need to be an instance of any of the classes, it only needs a discriminator value!

Installation

$ npm install --save @edcarroll/class-discriminators

Quickstart

Start by decorating your classes with their discriminator values:

import { DiscriminatorValue } from "@edcarroll/class-discriminators";

export class Hello {
    @DiscriminatorValue("hello")
    public readonly type = "hello";

    public sharedProp = 0;
    public helloProp = 10;
}

export class World {
    @DiscriminatorValue("world")
    public readonly type = "world";

    public sharedProp = 1;
    public worldProp = 11;
}

// HelloWorld is the type union we are going to be narrowing.
export type HelloWorld = Hello | World;

Next, configure a guard factory using configureGuardFactory by providing the type we are narrowing from:

import { configureGuardFactory } from "@edcarroll/class-discriminators";

const guardFactory = configureGuardFactory<HelloWorld>();

Note that you can optionally provide a property name to use as the discriminator property.

Now you can use the guard factory to test the type of various values.

Array Filtering

The usefulness of this library shows itself when filtering arrays. This library works with the type checker to ensure that when you run Array.filter it returns an array typed as the union of the provided types, so that you can then run functions.

An example use case would be with Redux / ngrx and class based actions.

const itemA = new Hello();
const itemB = new World();

// Take an array of multiple types
const arr = [itemA, itemB, itemA, itemB];

// You can use our configured guard factory to filter down to one or more specified types:
const hellos = arr
    .filter(guardFactory(Hello))
    .forEach(h => console.log(h.helloProp)); // prints 10, 10.

const worlds = console.log(arr
    .filter(guardFactory(World))
    .reduce((s, w) => w.worldProp + s, 0)); // prints 22.

// As mentioned before, `guardFactory` is a variadic higher order function,
// so you aren't limited to just 1 type:
const helloWorlds = arr
    .filter(guardFactory(Hello, World))
    .forEach(hw => console.log(hw.sharedProp)); // prints 0, 1, 0, 1.

// Also works well with Observables:
Observable
    .of(itemA, itemB, itemA, itemB)
    .filter(guardFactory(Hello))
    .subscribe(i => console.log(i.helloProp)); // prints 10, 10

Individual Guarding

Individual guarding is less useful as you can just switch on the discriminator property, however it works all the same:

// i.e. we have an item but know only it's a hello or a world.
const item:HelloWorld = new Hello();

if (guardFactory(Hello)(item)) {
    console.log(item.helloProp); // prints 10.
} else if (guardFactory(World)(item)) {
    console.log(item.worldProp); // prints 11.
}

// The above will print 10 as item is a `Hello` (not a surprise for us,
// but at least the type checker is happy!)

API

@DiscriminatorValue(value:string):PropertyDecorator

Property decorator that configures a class with the provided discriminator value. Decorator must be applied to the discriminator property as in the examples above.

configureGuardFactory<T>(discriminatorProperty?:keyof T):GuardFactory<T, ...U extends T>

Takes an optional property name override (ommitting this value uses the property the @DiscriminatorValue decorator was applied to) to use for discriminator value checks, and returns a GuardFactory function.

GuardFactory<T, ...U extends T>(...types:Class<U>[]):Guard<T, U>

Takes n classes decorated with @DiscriminatorValue and returns a Guard.

Note that the ...U above is not a real variadic kind, it's emulated using a lot of (up to 15) overflow declarations.

Guard<T, U>(guardee:T) => guardee is U

Takes an object of type T and returns true if its discriminator value is equal to one of the ones passed into the guard factory.

extractDiscriminators<T>(types:Class<T>[]):string[]

Takes an array of types and returns an array of their discriminator values.