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

ts-matches

v6.1.0

Published

We want to bring in some pattern matching into the typescript land. We want to be able to type guard a whole bunch of conditions and get the type back.

Downloads

3,875

Readme

Typescript Matches

codecov Bundle Phobia Bundle Phobia

Living Documentation https://runkit.com/blu-j/ts-matches

Uses

  • Schema Validation (parsers: like matches.string)
  • Schema Switching
  • Pattern matching
  • Switches as expressions

Related Libs

  • https://github.com/Blu-J/ts-matches-json-schema/ A library to be able to serialize + deserialize the types from json schema <-> ts-matches

Tech Used

Wiki Pattern Matching

Also useful for casting and boundary verifications. So using this as a json validator. The benefit comes that the parser becomes a validator, also the types are given back to typescript, where something like ajv cannot do or alot of validators.

Examples

The easiest and most useful feature is using the matcher as a validation. Here I want to validate that the shape is correct or throw an error

import matches from "https://deno.land/x/ts_matches/mod";
// could also use matches.shape here as well
const goldFishMatcher = matches.object({
  type: t.literal("gold-fish"),
  position: t.tuple(t.number, t.number),
  age: t.natural,
  name: t.string,
});
// For this example I'm making the shape less known
const input: object = {
  type: "gold-fish",
  position: [2, 3],
  age: 5,
  name: "Nemo",
};
// The matcher will know that the type returned is always the correct shape, and the type will reflect that
const checkedInput = goldFishMatcher.unsafeCast(input);

A variation is to use the guard version.

import matches from "ts-matches";
const goldFishMatcher = matches.object({
  type: t.literal("gold-fish"),
  position: t.tuple(t.number, t.number),
  age: t.natural,
  name: t.string,
});
// For this example I'm making the shape less known
const input: object = {
  type: "gold-fish",
  position: [2, 3],
  age: 5,
  name: "Nemo",
};
if (!goldFishMatcher.test(input)) {
  return;
}
/// After this point typescript will know the shape will be intersecting the shape we defined in the matcher

This is useful on a boundary layer, like fetching a value. In that case we have no idea what the shape is, so we should do a check on that.

import matches from "https://deno.land/x/ts_matches/mod";
fetch("fishes.com/gold-fishes/12")
  .then((x) => x.json())
  .then(
    matches.object({
      type: t.literal("gold-fish"),
      position: t.tuple(t.number, t.number),
      age: t.natural,
      name: t.string,
    }).unsafeCast
  );

And when we get the value out it will either be the type that we want, or it will throw an error. The other use case is a pattern matching.

import matches from "matches";
const getText = (x: unknown): string =>
  matches(x)
    .when(matches.string, (value) => `Found string: ${value}`)
    .when(matches.number, (value) => `Found number + 1: ${value + 1}`)
    .defaultTo("no found type yet");

And here we can use the type checking and what do in that case. With destructuring, lots of abilities are there

import matches from "matches";
const matchNone = matches.tuple(matches.literal("none"));
const matchSome = matches.tuple(matches.literal("some"), matches.any);
type option = ReturnType<typeof matchNone.unsafeCast> | typeof matchSome._TYPE;
const matchInteger = matches.every(
  matchSome,
  matches.tuple(matches.any, matches.number)
);
const testValue = ["some", 3];
const currentValue = matches(testValue)
  .when(matchNone, () => 0)
  .when(matchInteger, ([, value]) => value + 1)
  .when(matchSome, () => 0)
  .defaultTo(0);

We can also use the matches to match on literals, or return literals

import matches from "matches";
const currentValue = matches("5" as const)
  .when("5", "6", "At 5 or 6")
  .unwrap(0);

API

Given that the default export is matches Then the type of matches is unkown -> matcherChain, and also has the properties on that function that return a parser or a function that creates a parser

| Attribute | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------- | | array | A parser of Parser<_, unknown[]> or @see arrayOf | | arrayOf | Testing that any array is good and filled with type passed in | | some | That one of the matchers pass | | tuple | That we match a tuple of parsers | | regex | That we match the passed in regex | | number | Number | | natural | Number > 0 and is integer | | isFunction | is a function | | object | is an object paser (Parser<_, object>) or @see shape | | string | is a string | | shape | Matches a shape of an object, shape({key: parser}) for optionals use .optional() and fallback use .defaultTo() | | partial | Matches a shape of maybe attributes | | literal | Matches an exact match | | every | Matches every match passed in | | guard | Custom function for testing | | any | is something | | boolean | is a boolean | | nill | is a null or undefined | | dictionary | sets of [parserForKey, parserForValue] to validate a dictionary/ mapped type | | recursive | A way of doing a recursive parser, passing the self. Note this requires the type before while creating, cannot go from creation side. | | deferred | A way of creating a type that we will be filling in later, will be using the typescript shape first to verify | | literals | One the literals passed through |

MatcherChain api

| Attribute | Description | | ------------- | --------------------------------------------------------------------------- | | when | Create a matching case, when match return value | | defaultTo | Fall through case, ensures all are caught | | defaultToLazy | Fall through case, ensures all are caught in lazy fashion | | unwrap | This assumes that all cases are matched (TS tries to throw errors for this) |

Parser api

| Attribute | Description | | ------------ | ---------------------------------------------------------------------- | | parse | Use this to turn a value into an either | | unsafeCast | Use this to get the value or throw an error | | castPromise | Cast into a promise | | optional | output type can now be null or undefined | | nullable | output type can now be null | | defaultTo | instead of creating a optional we fallback to a value | | onMismatch | On a error of previous parsing fall back to value passed | | withMismatch | On a error of previous parsing fall back to value fn passed | | refine | we want to add more tests to value, could change type to sub | | validate | we want to add more tests to value | | errorMessage | If validation would create an error, return error as string, else void | | test | A guard for the type, returns true if type is valid (as a x is type) | | rename | Set to a new name for the parser |

Parser.parserErrorAsString ( validationError: parserError ): string This is the exposed transform of the parserError to a string. Override this if you want to make the errors different.

And of of any matcher we two functions, refine and unsafe cast. Refine is useful when we want to check a condition, like is even. And the matcher is also a function which creates an either of our value as well.

Deploying

Use the npm version minor | major and push the tags up, Then publish via npm