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

ts-matcher

v0.1.4

Published

## What is it?

Downloads

26

Readme

TSMatcher

What is it?

It is a small library which improves switch statement from JavaScript/TypeScript.

Why?

I am spoiled by advanced match in Scala which can match deeply, on more cases at once or use guards. This library strives to improve very basic switch statement, closing the gap a bit between TypeScript and Scala (and other languages with powerful matching, like Haskell).

Show me code!

Let's implement a basic calculator:

import Match from 'ts-matcher';

type Operation = '+' | '-' | '*' | '/';

interface Computation {
  a: number;
  b: number;
  op: Operation;
  result: number;
}

// using TSMatcher library
const compute = (a: number, b: number, op: Operation): Computation =>
  Match(op)
    .case('+', () => a + b)
    .case('-', () => a - b)
    .case('*', () => a * b)
    .case('/', () => a / b)
    .execMap(result => ({a, b, op, result}));

// using plain old switch statement
const computeSwitch = (a: number, b: number, op: Operation): Computation => {
  let result;
  switch (op) {
    case '+':
      result = a + b;
      break;
    case '-':
      result = a - b;
      break;
    case '*':
      result = a * b;
      break;
    case '/':
      result = a / b;
      break;
  }
  return {a, b, op, result: <number>result};
};

compute(1, 2, '+'); // {a: 1, b: 2, op: '+', result: 3}

You can see that in many cases TSMatcher is more concise, yet more powerful, than the built-in switch. Example above mainly demonstrates an ability to use Matcher as an expression which is very common in functional languages. For more information read the features section.

Installation

You can use npm

npm i -S ts-matcher

or grab a compiled version from this repository in /dist/src directory.

Basic usage

Create a matcher similarly to how one writes a switch:

const animal = 'dog';
Match(animal)

then add cases:

  .case('spider', () => console.log('I don\'t like those.'))
  .case('dog', () => console.log('What a good boy!'))

and finally, don't forget to execute the matcher:

  .exec();

You should see a result of out little program printed out:

What a good boy!

If no case is successful an exception is thrown. Usually we use default to handle unmatched values.

Match(2)
  .case(0, () => 0)
  .case(1, () => 1)
  .default(() => 9)
  .exec(); // 9

The animal example could be further simplified by using execMap which allows us to do side-effects with the result (or to apply transformations):

Match(animal)
  .case('spider', () => `I don't like those.`)
  .case('dog', () => 'What a good boy!')
  .execMap(x => console.log(x));

Features

For more complete examples of usage please look at tests.

Short-circuit evaluation of cases

Only first successfully matched case will get evaluated.

Match(true)
  .case(true, () => console.log(0))
  .case(true, () => console.log(1))
  .exec(); // only prints "0"

Deep equality

By default an equality check of the case is deep.

interface AB {a: { b: number }}

const obj: AB = {a: {b: 5}};
Match(obj)
  .case({a: {b: 4}}, () => 'a')
  .case({a: {b: 5}}, () => 'b')
  .exec(); // 'b'

Guards

You can also "match" against a function. This usage is very close to multiple if statements chained by elsees. Nice thing is that you can mix classic case with conditional one caseGuarded.

Match(-5)
  .caseGuarded(x => x < 0, () => 'less')
  .case(0, () => 'zero')
  .caseGuarded(x => x > 0, () => 'more')
  .exec(); // 'less'

Comparison to multiple values

In some languages, like Scala, one can have multiple values in one case. With TSMatcher you can compare to multiple values too:

Match('c')
  .caseMulti(['a', 'd', 'e'], () => 2)
  .caseMulti(['b', 'c'], () => 1)
  .exec(); // 1

Processing result

To process (aka map) a result you can use the execMap chain-terminating method instead of exec.

Match(1)
  .case(0, () => 'bb')
  .default(() => 'ccc')
  .execMap(x => x.length); // 3

From case handler is returned 'ccc', then a function in execMap is called (passing it the 'ccc') and its result 3 is returned.

Equality checking

If package lodash.isequalwith is present, then this function is used. Otherwise === operator will be utilized. customizer can be passed to case and caseMulti to customize behaviour of equality checking. You can change equality checking like this:

import Match, { EqualityChecker } from 'ts-matcher';

// not well typed
const customEqualityFunction = (a: number, b: number, customizer: number) => a + customizer === b;
EqualityChecker.initialize(customEqualityFunction);
Match(1)
  .case(3, () => '1=3', 2)
  .exec(); // '1=3'

Development

Installing Dependencies

yarn

Running Tests

yarn test

Building

yarn build

Output is located in dist/src directory.

Drawbacks

Performance

As it is with majority of abstractions, it comes with a performance cost. If you require extremely high performance and/or are not willing to make a trade-off for better abstractions, then I don't recommend using this library. Please note that unless you plan on using it in a very tight loop (e.g. real-time rendering, computing animation in every frame, game loop or intensive data processing) then you are probably fine, since even with only 1ms of work inside switch/matcher impact of this library is for practical purposes non-existent (exactly same ops/s).

You can try yourself:

yarn run perf

The library could be improved to support "prepared" matcher objects, but at this time I have no need for it (I primarily write code for ordinary front-ends and this kind of performance is rarely needed).

Loss of type narrowing

If you rely in all your switches on type narrowing (tagged unions), then this library is not for you. I might look into it in future, but I am not sure if it is even possible.

License

MIT