5.4.1 • Published 1 year ago

ts-matches v5.4.1

Weekly downloads
172
License
MIT
Repository
github
Last release
1 year ago

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

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.ts";
// 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.ts";
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

AttributeDescription
arrayA parser of Parser<_, unknown[]> or @see arrayOf
arrayOfTesting that any array is good and filled with type passed in
someThat one of the matchers pass
tupleThat we match a tuple of parsers
regexThat we match the passed in regex
numberNumber
naturalNumber > 0 and is integer
isFunctionis a function
objectis an object paser (Parser<_, object>) or @see shape
stringis a string
shapeMatches a shape of an object, shape({key: parser}, ["key"], {"key": "fallbackValue"} as const) for optionals default fallbacks,shape({key: parser}, ["key"]) for optionals no defaults, shape({key: parser}) for every required
partialMatches a shape of maybe attributes
literalMatches an exact match
everyMatches every match passed in
guardCustom function for testing
anyis something
booleanis a boolean
nillis a null or undefined
dictionarysets of parserForKey, parserForValue to validate a dictionary/ mapped type
recursiveA way of doing a recursive parser, passing the self. Note this requires the type before while creating, cannot go from creation side.
deferredA way of creating a type that we will be filling in later, will be using the typescript shape first to verify
literalsOne the literals passed through

MatcherChain api

AttributeDescription
whenCreate a matching case, when match return value
defaultToFall through case, ensures all are caught
defaultToLazyFall through case, ensures all are caught in lazy fashion
unwrapThis assumes that all cases are matched (TS tries to throw errors for this)

Parser api

AttributeDescription
parseUse this to turn a value into an either
usafeCastUse this to get the value or throw an error
castPromiseCast into a promise
optionaloutput type is now a null of value
defaultToinstead of creating a optional we fallback to a value
refinewe want to add more tests to value, could change type to sub
validatewe want to add more tests to value
errorMessageIf validation would create an error, return error as string, else void
testA guard for the type, returns true if type is valid (as a x is type)
renameSet 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

5.3.1

1 year ago

5.3.0

1 year ago

5.4.1

1 year ago

5.4.0

1 year ago

5.2.1

2 years ago

5.2.0

2 years ago

5.1.8

2 years ago

5.1.7

2 years ago

5.1.6

2 years ago

5.1.5

2 years ago

5.1.4

2 years ago

5.1.3

2 years ago

5.1.2

2 years ago

5.1.0

2 years ago

5.1.0-2

2 years ago

5.1.0-1

2 years ago

5.1.0-0

3 years ago

5.0.0

3 years ago

4.2.0

3 years ago

4.1.5

3 years ago

4.1.4

3 years ago

4.1.3

3 years ago

4.1.0

3 years ago

4.0.2

3 years ago

4.0.1

3 years ago

4.0.0

3 years ago

3.5.4

3 years ago

3.5.3

3 years ago

3.5.2

3 years ago

3.5.1

4 years ago

3.5.0

4 years ago

3.2.1

6 years ago

3.2.0

6 years ago

3.1.0

6 years ago

3.0.0

6 years ago

2.1.2

6 years ago

2.1.1

6 years ago

2.1.0

6 years ago

2.0.0

6 years ago

1.3.4

6 years ago

1.3.1

6 years ago

1.3.0

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.0

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago