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 🙏

© 2026 – Pkg Stats / Ryan Hefner

runcheck

v1.15.0

Published

A tiny (less than 2 KiB Gzipped) and treeshakable! lib for typescript runtime type checks with autofix support

Readme

Runcheck

A lib for js/typescript runtime type checks with autofix support. Runcheck has the goal of being very lightweight and fast ⚡. Because of that, it has only around 2.9kb Gzipped (at v0.30), has no dependencies and is tree-shakeable!

Installation

pnpm add runcheck

Basic types:

| runcheck type | ts type equivalent | | -------------------------------- | ------------------------------------------------- | | rc_string | string | | rc_number | number | | rc_boolean | boolean | | rc_any | any | | rc_unknown | unknown | | rc_null | null | | rc_undefined | undefined | | rc_date | Date | | rc_instanceof(instance: T) | Classes typecheck in general | | rc_literals(...literals: T[]) | Type literal in general like hello, true, 1 | | rc_union(...types: T[]) | Union types in general like string \| 1 | | rc_array<T>(type: T) | T[] | | rc_loose_array<T>(type: T) | T[] (filters invalid items) | | rc_tuple<T>(...types: T[]) | [T, T] | | rc_record<T>(type: T) | Record<string, T> | | rc_loose_record<T>(type: T) | Record<string, T> (filters invalid values) | | rc_intersection(...types: T[]) | Intersection types like {a:string} & {b:string} |

Array types:

Array loose check

You can also use rc_loose_array to reject the wrong elements of an array and return the valid ones.

const shape = rc_loose_array(rc_string)

const input = ['hello', 1, 'world']

const result = rc_parse(input, shape)

// result.data will be ['hello', 'world']
// result.warnings will return the warnings about the invalid elements

Checking unique values

With the rc_array or rc_loose_array type you can also use the unique option to check if the array has no duplicated values.

const shape = rc_array(rc_string, { unique: true })

For arrays of objects, you can provide a string to unique option to check if the array items has no duplicated values of a specific property.

const shape = rc_array(rc_object({ id: rc_string }), { unique: 'id' })

You can also provide a function to unique option to check if the array items has no duplicated values based on a custom function return.

const shape = rc_array(
  rc_object({ id: rc_string, meta_id: rc_string.optional() }),
  {
    unique: (item) => item.meta_id || item.id,
  },
)

Object types:

rc_object

const shape = rc_object({
  name: rc_string,
  age: rc_number,
  isCool: rc_boolean,
  // nested objects
  address: {
    street: rc_string,
    number: rc_number,
  },
})

The rc_object will allow extra properties but any extra properties will be stripped in parsing. To allow extra properties in parsing, use rc_obj_extends.

Marking optional keys

Optional keys can be marked with the optionalKey() method.

const shape = rc_object({
  name: rc_string.optionalKey(),
  age: rc_number,
  isCool: rc_boolean,
})

/*
inferred type will be:
{
  name?: string | undefined,
  age: number,
  isCool: boolean,
}

instead of:
{
  name: string | undefined,
  age: number,
  isCool: boolean,
}
*/

rc_obj_strict

The same as rc_object but, any extra properties will be throw an error in parsing.

rc_obj_merge

Allow to merge two rc_object types. Example:

const shape = rc_obj_merge(
  rc_object({
    name: rc_string,
    age: rc_number,
    isCool: rc_boolean,
  }),
  rc_object({
    address: rc_string,
    phone: rc_string,
  }),
)

rc_record

Validates only the values of a object, equivalent to Record<string, T> in typescript.

const shape = rc_record(rc_number)

// shape type is `Record<string, number>`

// `rc_record` also accepts the following options:
const shape = rc_record(rc_number, {
  checkKey: (key) => key !== 'a', // Check if the key is valid
  looseCheck: true, // If true, the invalid keys will be striped
})

rc_loose_record

Validates only the values of a object, equivalent to Record<string, T> in typescript. But, it will reject invalid keys and return the valid ones.

const shape = rc_loose_record(rc_number)

Parsing

import { rc_parse } from 'runcheck'

const input = JSON.parse(jsonInput)

const parseResult = rc_parse(input, rc_array(rc_string))

if (parseResult.error) {
  throw new Error(parseResult.errors.join('\n'))
  // Errors are a array of strings
}

const result = parseResult.data
// Do something with result

You can also use rc_parser to create a reusable parser.

import { rc_parser } from 'runcheck'

const parser = rc_parser(rc_array(rc_string))

const parseResult = parser(jsonInput)
const parseResult2 = parser(jsonInput2)

Strict parsing

Use the strict option to disable autofix and fallback

const parseResult = rc_parse(
  input,
  // fallback will be ignored
  rc_array(rc_string).withFallback([]),
  {
    strict: true,
  },
)

Type assertion

Use rc_is_valid and rc_validator to do a simple type assertion.

import { rc_is_valid } from 'runcheck'

const input = JSON.parse(jsonInput)

if (rc_is_valid(input, rc_array(rc_string))) {
  // input will be inferred by ts as `string[]`
}

Type assertion in a parse result

Use rc_assert_is_valid to do a simple type assertion in a parse result.

import { rc_assert_is_valid } from 'runcheck'

const input = JSON.parse(jsonInput)

const result = rc_parse(input, rc_array(rc_string))

rc_assert_is_valid(result)
// will throw an error if the result is invalid, otherwise will narrow the result type to a valid result

Loose parsing

You can now use the result methods unwrapOr and unwrapOrNull for more convenient loose parsing:

import { rc_parse } from 'runcheck'

const input = JSON.parse(jsonInput)

// Using unwrapOr method
const result = rc_parse(input, rc_array(rc_string)).unwrapOr([])
// will fallback to [] if the input is invalid

// Using unwrapOrNull method
const result2 = rc_parse(input, rc_array(rc_string)).unwrapOrNull()
// will fallback to null if the input is invalid

Strict parsing

You can use the unwrap() method directly on the parse result:

import { rc_parse, RcValidationError } from 'runcheck'

const input = JSON.parse(jsonInput)

try {
  const result = rc_parse(input, rc_array(rc_string)).unwrap()
  // result will be the parsed data or throw RcValidationError
} catch (error) {
  if (error instanceof RcValidationError) {
    // handle error
  }
}

Autofixing and fallback values in parsing

Values can be autofixed and fallback values can be provided for parsing. The checks will pass but the result will return warnings messages.

type SuccessResult = {
  error: false
  data: T
  warnings: string[] | false
}

Fallback

Use the method rc_[type].withFallback(fallback) to provide a fallback value if the input is not valid.

const input = 'hello'

const result = rc_parse(input, rc_string.withFallback('world'))

AutoFix

You can also use rc_[type].autoFix() to automatically fix the input if it is not valid.

const input = 1

const result = rc_parse(
  input,
  rc_string.autoFix((input) => input.toString()),
)

There are also some predefined autofixed types that you can import:

import {
  rc_string_autofix,
  rc_boolean_autofix,
  rc_number_autofix,
} from 'runcheck/autofixable'

// use like any other type

| Autofix type | Equivalent to | Autofixes | | -------------------- | ------------- | -------------------------------------------------- | | rc_boolean_autofix | boolean | 0 \| 1 \| 'true' \| 'false' \| null \| undefined | | rc_string_autofix | string | valid number inputs | | rc_number_autofix | number | valid numeric string inputs |

Performing custom checks

You can also use rc_[type].where(customCheckFunction) to perform custom checks.

const input = 1

const positiveNumberType = rc_number.where((input) => input > 0)

Infer types from schemas

You can use RcInferType<typeof schema> to infer the types from a schema.

const schema = rc_object({
  name: rc_string,
  age: rc_number,
  isCool: rc_boolean,
})

export type Person = RcInferType<typeof schema>

You can also use the RcPrettyInferType<typeof schema> to get a more readable type.

Type modifiers

You can use also modifiers like rc_string.optional() to extend the rc types:

| runcheck modifier | ts type equivalent | | ----------------------- | ------------------------ | | rc_[type].optional() | T \| undefined | | rc_[type].orNull() | T \| null | | rc_[type].orNullish() | T \| null \| undefined |

Recursive types

You can use rc_recursive to create recursive types. But the types can't be inferred in this case. So you need to provide the type manually.

type MenuTree = {
  name: string
  children: MenuTree[]
}

// the type should be provided manually to the variable in this case
const menuTreeSchema: RcType<MenuTree[]> = rc_array(
  rc_object({
    name: rc_string,
    // you can safely auto-reference the schema here
    children: rc_recursive(() => menuTreeSchema),
  }),
)

const result = rc_parse(input, menuTreeSchema)

Transform types

You can use rc_transform to validate an input and transform it to another data.

const input = 'hello'

const result = rc_parse(
  input,
  rc_transform(rc_string, (input) => input.length),
)

rc_unsafe_transform

For simpler cases where you don't need output validation, use rc_unsafe_transform:

const schema = rc_unsafe_transform(rc_string, (input) => input.toUpperCase())

const result = rc_parse('hello', schema)
if (result.ok) {
  console.log(result.data) // 'HELLO'
}

Transformed types which result can be validated with same schema

You may want to create a transformed type which result can be validated with the same schema. For this you can use the rc_narrow type. Example:

const stringOrArrayOfStrings = rc_union(rc_string, rc_array(rc_string))

const schema = rc_narrow(stringOrArrayOfStrings, (input) =>
  Array.isArray(input) ? input : [input],
)

const result = rc_parse('hello', schema)

if (result.ok) {
  // the schema can safely be used to validate the result too
  const transformedResult = rc_parse(result.data, schema)
}

Default types

You can use rc_default to provide a default value if the input is undefined.

const input = {
  name: 'John',
}

const result = rc_parse(
  input,
  rc_object({
    name: rc_string,
    age: rc_default(rc_number, 20),
  }),
)

if (result.ok) {
  result.data.age // = 20
}

If you need to use default in nullish values you can use rc_nullish_default.

Safe fallback types

You can use rc_safe_fallback to provide a fallback value that is validated against the schema, ensuring type safety.

const input = 'invalid'

const result = rc_parse(
  input,
  rc_safe_fallback(rc_number, 42), // 42 must be a valid number
)

if (result.ok) {
  result.data // = 42 (type-safe)
}

Advanced object types

rc_get_from_key_as_fallback

Allows to rename a key in a object. Example:

const shape = rc_object({
  // name will use the value of oldName if name is not present in input
  // which will rename `oldName` to `name` in the result
  name: rc_get_from_key_as_fallback('oldName', rc_string),
  age: rc_number,
  isCool: rc_boolean,
})

Snake case normalization

you can use rc_object with the normalizeKeysFrom option to normalize the keys of a object to snake case.

const shape = rc_object(
  {
    name: rc_string,
    age: rc_number,
    isCool: rc_boolean,
  },
  { normalizeKeysFrom: 'snake_case' },
)

rc_parse({ name: 'John', age: 20, is_cool: true }, shape) // will not return an error and will normalize the response to { name: 'John', age: 20, isCool: true }

rc_get_obj_shape

Extracts the object shape from an object type for inspection or manipulation:

const shape = rc_object({
  name: rc_string,
  age: rc_number,
  isCool: rc_boolean,
})

const objShape = rc_get_obj_shape(shape)
// objShape = { name: RcType<string>, age: RcType<number>, isCool: RcType<boolean> }
const nameSchema = objShape.name

rc_enable_obj_strict

Enables strict object validation for a type, rejecting excess properties:

const userSchema = rc_object({ name: rc_string, age: rc_number })
const strictUser = rc_enable_obj_strict(userSchema)
// Now rejects objects with excess properties

const result = strictUser.parse({ name: 'John', age: 30 }) // valid
const result2 = strictUser.parse({ name: 'John', age: 30, extra: 'data' }) // invalid

Use the nonRecursive option to only affect the immediate object type:

const strictUser = rc_enable_obj_strict(userSchema, { nonRecursive: true })

rc_obj_extends

Don't strip unchecked keys from the result. Example:

const shape = rc_object({
  name: rc_string,
})

const result = rc_parse(
  { name: 'John', age: 20, is_cool: true },
  rc_obj_extends(shape),
)

// keys `age` and `is_cool` will be present in the result
result.data // { name: 'John', age: 20, is_cool: true }

rc_obj_pick

Allows to pick a subset of a object schema. Example:

const shape = rc_object({
  name: rc_string,
  age: rc_number,
  isCool: rc_boolean,
})

const nameSchema = rc_obj_pick(shape, ['name'])

rc_obj_omit

Allows to omit a subset of a object schema. Example:

const shape = rc_object({
  name: rc_string,
  age: rc_number,
  isCool: rc_boolean,
})

const baseSchema = rc_obj_omit(shape, ['isCool'])

rc_obj_builder

Creates a rc_object from a type. This gives better error messages and autocompletion.

type SchemaType = {
  level1: {
    level2: {
      level3: {
        level4: {
          level5: number
        }
      }
    }
  }
  optionalObj?: {
    a: string
  }
  objOrNull: null | {
    a: string
  }
  objOrNullish:
    | null
    | undefined
    | {
        a: string
      }
}

const schema = rc_obj_builder<SchemaType>()({
  level1: {
    level2: {
      level3: {
        level4: {
          level5: rc_string,
          // better error here
        },
      },
    },
  },
  optionalObj: [
    'optional',
    {
      a: rc_string,
      // better error here and autocompletion :)
    },
  ],
  objOrNull: [
    'null_or',
    {
      a: rc_string,
    },
  ],
  objOrNullish: [
    'nullish_or',
    {
      a: rc_string,
    },
  ],
})

rc_discriminated_union

Creates a discriminated union type with faster check performance compared to rc_union.

const networkState = rc_discriminated_union('state', {
  loading: {},
  success: {
    response: rc_string,
  },
  error: {
    code: rc_number,
  },
})

const result = rc_unwrap(
  rc_parse({ state: 'success', response: 'hello' }, networkState),
)
// result will be inferred as:
// | { state: 'loading' }
// | { state: 'success', response: string }
// | { state: 'error', code: number }

rc_discriminated_union_builder

Creates a type-safe discriminated union builder that enforces the structure matches a TypeScript type:

type Shape =
  | { type: 'circle'; radius: number }
  | { type: 'rectangle'; width: number; height: number }

const shapeSchema = rc_discriminated_union_builder<Shape, 'type'>('type')({
  circle: { radius: rc_number },
  rectangle: { width: rc_number, height: rc_number },
})

rc_array_filter_from_schema

Creates a two passes array validation. The first will validate the items against the filter schema and filter the item. The second will perform the type check against the filtered items.

const schema = rc_array_filter_from_schema(
  // 1 validate the items against a filter schema
  rc_object({
    deleted: rc_boolean,
  }),
  // Then filter the items based on the filter schema result
  (item) => !item.deleted,

  // 2 validate the filtered items
  rc_object({
    value: rc_string,
  }),
)

const result = rc_parse(
  [
    { deleted: false, value: 'hello' },
    { deleted: true, value: 'world' },
  ],
  schema,
)

// result.value === [{ value: 'hello' }]

JSON Parsing

You can use rc_parse_json to parse and validate JSON strings directly:

import { rc_parse_json } from 'runcheck'

const userSchema = rc_object({ name: rc_string, age: rc_number })
const result = rc_parse_json('{"name":"John","age":30}', userSchema)

if (result.ok) {
  console.log(result.data) // { name: 'John', age: 30 }
}

Standard Schema Integration

Runcheck supports Standard Schema V1 for interoperability with other validation libraries:

Converting to Standard Schema

import { rc_to_standard } from 'runcheck'

const rcSchema = rc_object({ name: rc_string, age: rc_number })
const standardSchema = rc_to_standard(rcSchema)

// Use with other Standard Schema compatible libraries

Converting from Standard Schema

import { rc_from_standard } from 'runcheck'

// Convert a Standard Schema to runcheck type
const rcSchema = rc_from_standard(someStandardSchema)

Additional Utilities

Type Guards

Use isRcType to check if a value is a runcheck type:

import { isRcType } from 'runcheck'

if (isRcType(someValue)) {
  // someValue is now typed as RcType<any>
  const result = someValue.parse(input)
}

Schema Inspection

Use getSchemaKind to get the string representation of a schema:

import { getSchemaKind } from 'runcheck'

const kind = getSchemaKind(rc_string) // returns 'string'
const kind2 = getSchemaKind(rc_array(rc_number)) // returns 'number[]'