{"id":22230199,"url":"https://github.com/maluscat/runtime-type-check","last_synced_at":"2026-01-27T01:01:19.814Z","repository":{"id":261339481,"uuid":"883965060","full_name":"Maluscat/runtime-type-check","owner":"Maluscat","description":"[MIRROR] Modular runtime type checker with focus on creating readable and smart error messages","archived":false,"fork":false,"pushed_at":"2024-11-10T17:04:48.000Z","size":164,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-27T04:46:05.816Z","etag":null,"topics":["assert","javascript","type-checking","typescript"],"latest_commit_sha":null,"homepage":"https://gitlab.com/Maluscat/runtime-type-check","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Maluscat.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-05T22:24:31.000Z","updated_at":"2024-12-22T19:55:24.000Z","dependencies_parsed_at":"2024-11-06T03:04:26.802Z","dependency_job_id":"aca27731-2266-4401-bd27-c42caeede960","html_url":"https://github.com/Maluscat/runtime-type-check","commit_stats":null,"previous_names":["maluscat/runtime-type-check"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Maluscat/runtime-type-check","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maluscat%2Fruntime-type-check","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maluscat%2Fruntime-type-check/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maluscat%2Fruntime-type-check/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maluscat%2Fruntime-type-check/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Maluscat","download_url":"https://codeload.github.com/Maluscat/runtime-type-check/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maluscat%2Fruntime-type-check/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28794529,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T21:49:50.245Z","status":"ssl_error","status_checked_at":"2026-01-26T21:48:29.455Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["assert","javascript","type-checking","typescript"],"created_at":"2024-12-03T01:14:38.818Z","updated_at":"2026-01-27T01:01:19.789Z","avatar_url":"https://github.com/Maluscat.png","language":"JavaScript","readme":"# RuntimeTypeCheck\nMinimal, modular type checker for the runtime with a heavy focus on producing\nreadable and smart error messages.\n\n\n\n## Installation\nSince this library is completely runtime agnostic, it can be used inside any\nJavaScript environment, including the web.\n\n### Download\nThe only required file is `RuntimeTypeCheck.js` inside the [`script`](./script)\nfolder. If you want type checking, fetch `RuntimeTypeCheck.d.ts` as well!\n\n### npm\nAvailable on npm under `@maluscat/runtime-type-check`. Use your favorite package manager:\n```sh\nyarn add @maluscat/runtime-type-check\nbun install @maluscat/runtime-type-check\nnpm install @maluscat/runtime-type-check\n```\n\n\n## Concepts\nThe core concept of RuntimeTypeCheck is a `Condition`, which is a building\nblock that contains assertion information. Conditions can recursively extend\nother conditions and can be combined \"OR\" and \"AND\" wise.\n\nThis allows an overarching assertion to be split up into multiple smaller\nconditions, ensuring flexibility and reusability. This means that conditions\nare able to focus on only one part of an assertion while safely assuming the\npassed value to already match various other layers.\nFor example, a condition `divisible(n)` could extend the condition\n`number` and can thus safely assume that any passed value is a number.\n\nSo, if we want to assert a value to be a positive number divisible by 5,\nwe can AND-combine two assertions `positive` and `divisible(5)`, both of which\nwill extend the condition `number` (see the [examples](#examples) below).\n\n\n### Condition\nThe TS type of a condition looks like this:\n```ts\ninterface Condition {\n  /** Assertion function. */\n  assert: (value: any) =\u003e boolean;\n  /**\n   * Conditions that this condition relies on.\n   * Note that this field is a Descriptor, so an \"OR\" list of \"AND\" conditions.\n   */\n  conditions?: Descriptor;\n  /**\n   * Description of what the correct type should be.\n   *\n   * This will be merged with other conditions to form a coherent sentence\n   * of the desired type (e.g. \"Expected a positive number of length 5\").\n   */\n  shouldBe: Message;\n  /**\n   * Generic description of any value that does **not** assert,\n   * so the opposite of what is asserted for.\n   *\n   * E.g. \"a floating point number\" when asserting an integer.\n   */\n  is: string | ((data: IsData) =\u003e string);\n}\n```\nwhere\n```ts\n/** Will be merged into a sentence of the form \"...before type ...after\" */\ninterface Message {\n  /** Will be put before the type (e.g. \"positive\") */\n  before?: string;\n  /** A noun (e.g. \"integer\" or \"string\") */\n  type?: string;\n  /** Will be put after the type (e.g. \"of length 5\") */\n  after?: string;\n}\ninterface IsData {\n  val: any;\n  type: Type;\n  article: 'a' | 'an';\n}\ntype Type =\n  | 'array' | 'NaN' | 'null' | 'string' | 'number' | 'bigint'\n  | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function';\n```\n\nSee below for a more detailed overview with examples, and the [docs](#docs)\nfor more in-depth descriptions.\n\n\n## Usage\nThe only non-typing-related exports are `RuntimeTypeCheck` (main library),\n`Cond` (predefined conditions) and, if needed, `TypeCheckError` (thrown by\n`assertAndThrow`):\n```js\nimport { RuntimeTypeCheck, Cond, TypeCheckError } from '@maluscat/runtime-type-check';\n```\nSee the [docs](#docs) for an overview of all additional typing related exports\nfor use in TypeScript.\n\n`RuntimeTypeCheck` is an entirely static class. For most use cases, there are\nonly two relevant methods: `assert` (returns boolean) and `assertAndThrow`\n(throws an explanatory error message when it does not assert).\n\n### Parameters\n**Every method accepts its conditions as a rest parameter**, *any*\nof which may assert (OR). One parameter can either be a single\ncondition or an AND array of conditions.\n\nHence, this matches either a string OR a number:\n```ts\nRuntimeTypeCheck.assert(3, Cond.string, Cond.number)\n```\nWhereas this matches a positive number:\n```ts\nRuntimeTypeCheck.assert(3, [ Cond.positive, Cond.number ])\n```\n\nThis also applies to the `conditions` parameter of a `Condition`.\n\n### `assert(value, ...descriptor)`\nReturns a boolean of whether the passed value matches the passed descriptor.\n\n### `assertAndThrow(value, ...descriptor)`\nIf the given value does not assert, `assertAndThrow` throws an error message\nthat automatically catches the most relevant condition in the context of the\ngiven value. So, when asserting, say, either a string or a positive integer\nagainst the value `-3`, the method will explain that the given *number* may\nnot be negative. See the [examples](#examples) for a more detailed overview.\n\nIf you need to modify or catch a potentially thrown error, it is good practice\nto test the caught error for an instance of `TypeCheckError`.\nYou can then use its `message` field as-is or access the two parts of the\nmessage: `is` and `expected`:\n```ts\ntry {\n  RuntimeTypeCheck.assertAndThrow(-3, Cond.string, Cond.false);\n} catch (err) {\n  if (err instanceof TypeCheckError) {\n    // message:  \"Expected string OR false, got number\"\n    // expected: \"string OR false\"\n    // is:       \"number\"\n    console.log(err.expected, err.is, err.message);\n  } else throw err;\n}\n```\n\n\n### `Cond`\n`Cond` (alias: `RuntimeTypeCheck.Cond`) pre-defines commonly used conditions.\nSee an overview in the [docs](#docs).\n\nThere are also [additional conditions](#additional-conditions) included below\nthat have not made it into the library.\n\n\n## Examples\n### Using provided `Cond`\nLet's assert a value to be either a positive integer or a string. For this,\nthe conditions provided by the [`Cond`](https://docs.malus.zone/runtime-type-check/#Cond)\nclass can be used.\n```js\nimport { RuntimeTypeCheck, Cond } from '@maluscat/runtime-type-check';\n\n// true\nRuntimeTypeCheck.assert('foobar', Cond.string, [ Cond.positive, Cond.integer ]);\n\n// false (not positive)\nRuntimeTypeCheck.assert(-3, Cond.string, [ Cond.positive, Cond.integer ]);\n\n// TypeCheckError: \"Expected positive integer OR string, got a negative number or 0\"\nRuntimeTypeCheck.assertAndThrow(-3, Cond.string, [ Cond.positive, Cond.integer ]);\n```\n\n### Array with inner type\n`Cond` also provides conditions for array and object (both of which are functions!)\nthat can take a descriptor of their inner values:\n```js\n// true\nRuntimeTypeCheck.assertAndThrow([ 'foobar' ], Cond.array(Cond.string))\n\n// TypeCheckError: \"Expected Array\u003cstring\u003e, got number\"\nRuntimeTypeCheck.assertAndThrow(5, Cond.array(Cond.string))\n```\n\nBecause of the conditions' dynamic nature, this can be nested and combined:\n```js\n// true for both (Array\u003cstring[], number\u003e)\nRuntimeTypeCheck.assertAndThrow([['foobar']], Cond.array(Cond.array(Cond.string), Cond.number));\nRuntimeTypeCheck.assertAndThrow([69], Cond.array(Cond.array(Cond.string), Cond.number));\n\n// TypeCheckError: \"Expected Array\u003cArray\u003cstring\u003e OR number\u003e, got number\"\nRuntimeTypeCheck.assertAndThrow(5, Cond.array(Cond.array(Cond.string), Cond.array));\n```\n\n### Defining custom conditions\nNow we want to assert a number that's divisible by 5 and is greater than 25.\nThe builtin `Cond` does not provide any help here, so we can define the\nrequired conditions ourselves.\n\nBoth conditions should extend `Cond.number` to be sure that any incoming\nvalues are already numbers.\nWe will also take the liberty and make both of them a generic generator:\n```ts\nconst divisibleBy = (divisor: number) =\u003e ({\n  conditions: [ Cond.number ], // Ensure that it's a number\n  assert: (val: number) =\u003e val % divisor === 0,\n  shouldBe: { after: `that is divisible by ${divisor}` },\n  is: `a number not divisible by ${divisor}`\n} satisfies Condition) as Condition;\n\nconst greaterThan = (value: number) =\u003e ({\n  conditions: [ Cond.number ],\n  assert: (val: number) =\u003e val \u003e value,\n  shouldBe: { after: `that is greater than ${value}` },\n  is: `a number less than or equal to ${value}`\n} satisfies Condition) as Condition;\n```\nWe can now instantiate these functions to generate the conditions we want\nand combine them:\n```ts\nconst divisibleBy5 = divisibleBy(5);\nconst greaterThan25 = greaterThan(25);\n\nconst divisibleBy5AndGreaterThan25 = [ divisibleBy5, greaterThan25 ];\n```\nNow the condition is usable anywhere.\n```js\n// true\nRuntimeTypeCheck.assertAndThrow(30, divisibleBy5AndGreaterThan25);\n\n/*\n* TypeCheckError:\n*   Expected number that is divisible by 5 and is greater than 25,\n*   got a number less than or equal to 25\n*/\nRuntimeTypeCheck.assertAndThrow(25, divisibleBy5AndGreaterThan25);\n\n/*\n* TypeCheckError:\n*   Expected number that is divisible by 5 and is greater than 25,\n*   got a number not divisible by 5\n*/\nRuntimeTypeCheck.assertAndThrow(26, divisibleBy5AndGreaterThan25);\n```\n\nObviously, this can also be combined with other conditions.\nIf conditions are equally faulty, the first of them contributes its message.\n```js\n/*\n* TypeCheckError:\n*   Expected positive number that is divisible by 5 and is greater than 25,\n*   got a negative number or 0\n*/\nRuntimeTypeCheck.assertAndThrow(-6, [ Cond.positive, ...divisibleBy5AndGreaterThan25 ]);\n\n/*\n* TypeCheckError:\n*   Expected positive number that is divisible by 5 and is greater than 25,\n*   got a number not divisible by 5\n*/\nRuntimeTypeCheck.assertAndThrow(-6, [ divisibleBy5, ...divisibleBy5AndGreaterThan25 ]);\n```\n\n\n\n## Docs\nSee the generated [docs](https://docs.malus.zone/runtime-type-check/) for a more\nin-depth overview of the library.\n- [`Cond`](https://docs.malus.zone/runtime-type-check/#Cond)\n- [`RuntimeTypeCheck`](https://docs.malus.zone/runtime-type-check/#RuntimeTypeCheck)\n\n\n## Additional conditions\nHere are some useful conditions not provided by the base library that can just\nbe copy pasted into your own code if you need them!\nThis is because RuntimeTypeCheck is to be kept as light weight as possible.\n\n```ts\n/**\n * Assert a value to be not negative (0 or more).\n * Implies {@link Cond.number}.\n */\nconst nonnegative = {\n  conditions: [ Cond.number ],\n  assert: (val: number) =\u003e val \u003e= 0,\n  shouldBe: { before: 'non-negative' },\n  is: 'a negative number'\n};\n```\n```ts\n/**\n * Generate a condition that asserts a value to be inside\n * the given interval (inclusive). Implies {@link Cond.number}.\n *\n * @param min Lower interval boundary (inclusive)\n * @param max Upper interval boundary (inclusive)\n */\nconst range = (min: number, max: number) =\u003e ({\n  conditions: [ Cond.number ],\n  assert: (val: number) =\u003e val \u003e= min \u0026\u0026 val \u003c= max,\n  shouldBe: { after: `of the interval [${min}, ${max}]` },\n  is: 'a number outside of the required range'\n});\n```\n```ts\n/**\n * Generate a condition that asserts a value to be divisible\n * by the given divisor. Implies {@link Cond.number}.\n */\nconst divisibleBy = (divisor: number) =\u003e ({\n  conditions: [ Cond.number ], // Ensure that it's a number\n  assert: (val: number) =\u003e val % divisor === 0,\n  shouldBe: { after: `that is divisible by ${divisor}` },\n  is: `a number not divisible by ${divisor}`\n});\n```\n```ts\n/**\n * Generate a condition that asserts a value to be greater\n * than the given value. Implies {@link Cond.number}.\n */\nconst greaterThan = (value: number) =\u003e ({\n  conditions: [ Cond.number ],\n  assert: (val: number) =\u003e val \u003e value,\n  shouldBe: { after: `that is greater than ${value}` },\n  is: `a number less than or equal to ${value}`\n});\n```\n\n\n## Dev fact\nThis project incubated within [Slider89](https://github.com/Maluscat/Slider89), with\n[this](https://github.com/Maluscat/Slider89/blob/62529f5d2bc83ba311876b86f592f6a5f988ad57/src/core/type-check/RuntimeTypeCheck.ts)\nbeing the last public point of reference. As the rewrite of this version was\nfinished, I immediately rewrote it again with an even better approach, so the\ncurrent version is technically the third major iteration.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaluscat%2Fruntime-type-check","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaluscat%2Fruntime-type-check","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaluscat%2Fruntime-type-check/lists"}