{"id":23159292,"url":"https://github.com/blu-j/ts-matches","last_synced_at":"2025-08-18T02:31:07.603Z","repository":{"id":41142901,"uuid":"147714910","full_name":"Blu-J/ts-matches","owner":"Blu-J","description":"Being able to pattern match in typescript","archived":false,"fork":false,"pushed_at":"2024-06-03T16:34:39.000Z","size":2419,"stargazers_count":11,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-09-19T11:19:34.309Z","etag":null,"topics":["either","matcher","maybe","pattern","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Blu-J.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-06T18:15:56.000Z","updated_at":"2024-06-03T16:34:43.000Z","dependencies_parsed_at":"2024-06-03T19:13:20.762Z","dependency_job_id":null,"html_url":"https://github.com/Blu-J/ts-matches","commit_stats":{"total_commits":634,"total_committers":11,"mean_commits":57.63636363636363,"dds":0.613564668769716,"last_synced_commit":"165148e36b9a650e9114730ae0c23fffa4db55f2"},"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blu-J%2Fts-matches","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blu-J%2Fts-matches/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blu-J%2Fts-matches/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blu-J%2Fts-matches/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Blu-J","download_url":"https://codeload.github.com/Blu-J/ts-matches/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230192198,"owners_count":18187874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["either","matcher","maybe","pattern","typescript"],"created_at":"2024-12-17T22:37:02.999Z","updated_at":"2025-08-18T02:31:07.588Z","avatar_url":"https://github.com/Blu-J.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Typescript Matches\n\n[![codecov](https://codecov.io/gh/Blu-J/ts-matches/branch/master/graph/badge.svg?token=RQ37H4AWWR)](https://codecov.io/gh/Blu-J/ts-matches)\n![Bundle Phobia](https://badgen.net/bundlephobia/minzip/ts-matches)\n![Bundle Phobia](https://badgen.net/bundlephobia/min/ts-matches)\n\nLiving Documentation https://runkit.com/blu-j/ts-matches\n\n# Uses\n\n- Schema Validation (parsers: like matches.string)\n- Schema Switching\n- Pattern matching\n- Switches as expressions\n\n# Related Libs\n\n- https://github.com/Blu-J/ts-matches-json-schema/ A library to be able to\n  serialize + deserialize the types from json schema \u003c-\u003e ts-matches\n\n## Tech Used\n\n[Wiki Pattern Matching](https://en.wikipedia.org/wiki/Pattern_matching)\n\nAlso useful for casting and boundary verifications. So using this as a json\nvalidator. The benefit comes that the parser becomes a validator, also the types\nare given back to typescript, where something like ajv cannot do or alot of\nvalidators.\n\n## Examples\n\nThe easiest and most useful feature is using the matcher as a validation. Here I\nwant to validate that the shape is correct or throw an error\n\n```typescript\nimport matches from \"https://deno.land/x/ts_matches/mod\";\n// could also use matches.shape here as well\nconst goldFishMatcher = matches.object({\n  type: t.literal(\"gold-fish\"),\n  position: t.tuple(t.number, t.number),\n  age: t.natural,\n  name: t.string,\n});\n// For this example I'm making the shape less known\nconst input: object = {\n  type: \"gold-fish\",\n  position: [2, 3],\n  age: 5,\n  name: \"Nemo\",\n};\n// The matcher will know that the type returned is always the correct shape, and the type will reflect that\nconst checkedInput = goldFishMatcher.unsafeCast(input);\n```\n\nA variation is to use the guard version.\n\n```typescript\nimport matches from \"ts-matches\";\nconst goldFishMatcher = matches.object({\n  type: t.literal(\"gold-fish\"),\n  position: t.tuple(t.number, t.number),\n  age: t.natural,\n  name: t.string,\n});\n// For this example I'm making the shape less known\nconst input: object = {\n  type: \"gold-fish\",\n  position: [2, 3],\n  age: 5,\n  name: \"Nemo\",\n};\nif (!goldFishMatcher.test(input)) {\n  return;\n}\n/// After this point typescript will know the shape will be intersecting the shape we defined in the matcher\n```\n\nThis is useful on a boundary layer, like fetching a value. In that case we have\nno idea what the shape is, so we should do a check on that.\n\n```typescript\nimport matches from \"https://deno.land/x/ts_matches/mod\";\nfetch(\"fishes.com/gold-fishes/12\")\n  .then((x) =\u003e x.json())\n  .then(\n    matches.object({\n      type: t.literal(\"gold-fish\"),\n      position: t.tuple(t.number, t.number),\n      age: t.natural,\n      name: t.string,\n    }).unsafeCast,\n  );\n```\n\nAnd when we get the value out it will either be the type that we want, or it\nwill throw an error. The other use case is a pattern matching.\n\n```typescript\nimport matches from \"matches\";\nconst getText = (x: unknown): string =\u003e\n  matches(x)\n    .when(matches.string, (value) =\u003e `Found string: ${value}`)\n    .when(matches.number, (value) =\u003e `Found number + 1: ${value + 1}`)\n    .defaultTo(\"no found type yet\");\n```\n\nAnd here we can use the type checking and what do in that case. With\ndestructuring, lots of abilities are there\n\n```typescript\nimport matches from \"matches\";\nconst matchNone = matches.tuple(matches.literal(\"none\"));\nconst matchSome = matches.tuple(matches.literal(\"some\"), matches.any);\ntype option = ReturnType\u003ctypeof matchNone.unsafeCast\u003e | typeof matchSome._TYPE;\nconst matchInteger = matches.every(\n  matchSome,\n  matches.tuple(matches.any, matches.number),\n);\nconst testValue = [\"some\", 3];\nconst currentValue = matches(testValue)\n  .when(matchNone, () =\u003e 0)\n  .when(matchInteger, ([, value]) =\u003e value + 1)\n  .when(matchSome, () =\u003e 0)\n  .defaultTo(0);\n```\n\nWe can also use the matches to match on literals, or return literals\n\n```typescript\nimport matches from \"matches\";\nconst currentValue = matches(\"5\" as const)\n  .when(\"5\", \"6\", \"At 5 or 6\")\n  .unwrap(0);\n```\n\n## API\n\nGiven that the default export is `matches` Then the type of `matches` is\n`unkown -\u003e matcherChain`, and also has the properties on that function that\nreturn a `parser` or a function that creates a `parser`\n\n| Attribute  | Description                                                                                                                           |\n| ---------- | ------------------------------------------------------------------------------------------------------------------------------------- |\n| array      | A parser of Parser\u003c\\_, unknown[]\u003e or @see arrayOf                                                                                     |\n| arrayOf    | Testing that any array is good and filled with type passed in                                                                         |\n| some       | That one of the matchers pass                                                                                                         |\n| tuple      | That we match a tuple of parsers                                                                                                      |\n| regex      | That we match the passed in regex                                                                                                     |\n| number     | Number                                                                                                                                |\n| natural    | Number \u003e 0 and is integer                                                                                                             |\n| isFunction | is a function                                                                                                                         |\n| object     | is an object paser (Parser\u003c\\_, object\u003e) or @see shape                                                                                 |\n| string     | is a string                                                                                                                           |\n| shape      | Matches a shape of an object, `shape({key: parser})` for optionals use .optional() and fallback use .defaultTo()                      |\n| partial    | Matches a shape of maybe attributes                                                                                                   |\n| literal    | Matches an exact match                                                                                                                |\n| every      | Matches every match passed in                                                                                                         |\n| guard      | Custom function for testing                                                                                                           |\n| any        | is something                                                                                                                          |\n| boolean    | is a boolean                                                                                                                          |\n| nill       | is a null or undefined                                                                                                                |\n| dictionary | sets of [parserForKey, parserForValue] to validate a dictionary/ mapped type                                                          |\n| recursive  | A way of doing a recursive parser, passing the self. Note this requires the type before while creating, cannot go from creation side. |\n| deferred   | A way of creating a type that we will be filling in later, will be using the typescript shape first to verify                         |\n| literals   | One the literals passed through                                                                                                       |\n\n`MatcherChain` api\n\n| Attribute     | Description                                                                 |\n| ------------- | --------------------------------------------------------------------------- |\n| when          | Create a matching case, when match return value                             |\n| defaultTo     | Fall through case, ensures all are caught                                   |\n| defaultToLazy | Fall through case, ensures all are caught in lazy fashion                   |\n| unwrap        | This assumes that all cases are matched (TS tries to throw errors for this) |\n\n`Parser` api\n\n| Attribute    | Description                                                            |\n| ------------ | ---------------------------------------------------------------------- |\n| parse        | Use this to turn a value into an either                                |\n| unsafeCast   | Use this to get the value or throw an error                            |\n| castPromise  | Cast into a promise                                                    |\n| optional     | output type can now be null or undefined                               |\n| nullable     | output type can now be null                                            |\n| defaultTo    | instead of creating a optional we fallback to a value                  |\n| onMismatch   | On a error of previous parsing fall back to value passed               |\n| withMismatch | On a error of previous parsing fall back to value fn passed            |\n| refine       | we want to add more tests to value, could change type to sub           |\n| validate     | we want to add more tests to value                                     |\n| errorMessage | If validation would create an error, return error as string, else void |\n| test         | A guard for the type, returns true if type is valid (as a `x is type`) |\n| rename       | Set to a new name for the parser                                       |\n\n`Parser.parserErrorAsString` ( validationError: parserError ): string This is\nthe exposed transform of the parserError to a string. Override this if you want\nto make the errors different.\n\nAnd of of any matcher we two functions, refine and unsafe cast. Refine is useful\nwhen we want to check a condition, like is even. And the matcher is also a\nfunction which creates an either of our value as well.\n\n## Deploying\n\nUse the `npm version minor | major` and push the tags up, Then publish via npm\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblu-j%2Fts-matches","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblu-j%2Fts-matches","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblu-j%2Fts-matches/lists"}