SkillAgentSearch skills...

Valita

A typesafe validation & parsing library for TypeScript.

Install / Use

/learn @badrap/Valita
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

@badrap/valita CI npm JSR

A TypeScript library for validating & parsing structured objects. The API is heavily influenced by Zod's excellent API, while the implementation side aims for the impressive performance of simple-runtypes.

const vehicle = v.union(
  v.object({ type: v.literal("plane"), airline: v.string() }),
  v.object({ type: v.literal("train") }),
  v.object({ type: v.literal("automobile"), make: v.string() }),
);
vehicle.parse({ type: "bike" });
// ValitaError: invalid_literal at .type (expected "plane", "train" or "automobile")

[!NOTE] While this package is still evolving, we're currently not accepting any new feature requests or suggestions. Please use the issue tracker for bug reports and security concerns, which we highly value and welcome. Thank you for your understanding ❤️

Goals and Non-Goals

Goals

  1. Input Validation & Parsing: The fundamental goal of the library is to ensure that incoming data, which might not be from a trusted source, aligns with the predetermined format.
  2. Minimalism: Deliver a streamlined and concentrated library that offers just the essentials.
  3. Extensibility: Allow users to create their own validators and parsers that cater to specific validation scenarios.

Non-Goals

  1. Data Definition: The library is designed to validate and parse input data as it enters the program, rather than serving as an exhaustive tool for defining all types within the program after obtaining input.
  2. Extensive Built-In Formats: The library does not prioritize having a large array of built-in validation formats out of the box.
  3. Asynchronous Parsing: Asynchronous operations are outside the scope for this library.

Installation

For Node.js

npm i @badrap/valita

For Deno

deno add @badrap/valita

API Reference

This section contains an overview of all validation methods.

Primitive Types

Let's start with the basics! Like every validation library we support all primitive types like strings, numbers, booleans and more. For example the v.string() primitive can be used like this to check whether some input value is a string:

import * as v from "@badrap/valita";

const t = v.string();
t.parse("Hello, World!");
// "Hello, World!"

Try to parse anything that's not a string and you get an error:

t.parse(1);
// ValitaError: invalid_type at . (expected string)

.parse(...) is typed in a way that it accepts any type of input value, but returns a properly typed value on success:

const u: unknown = "Hello, World!";

// TypeScript type checking is happy with this!
const s: string = t.parse(u);

The primitive types are:

  • v.string(): Check that the value is a string.
  • v.number(): Check that the value is a number (i.e. typeof value === "number", which includes NaN and ±Infinity).
  • v.bigint(): Check that the value is a bigint (i.e. typeof value === "bigint")
  • v.boolean(): Check that the value is a boolean.
  • v.null(): Check that the value is null.
  • v.undefined(): Check that the value is undefined.

Literal Types

Sometimes knowing if a value is of a certain type is not enough. We can use the .literal() method to check for actual values, like checking if a string is either "red", "green" or "blue" and not just any string.

const rgb = v.union(v.literal("red"), v.literal("green"), v.literal("blue"));

rgb.parse("green");
// "green"

rgb.parse("magenta");
// ValitaError: invalid_literal at . (expected "red", "green" or "blue")

We can also use this to check for concrete numbers, bigint literals or boolean values:

v.literal(1); // must be number 1
v.literal(1n); // must be bigint 1n
v.literal(true); // must be true

For more complex values you can use the .assert()-method. Check out Custom Validators to learn more about it.

Allow Any / Forbid All

Valita doesn't contain a built-in equivalent to TypeScript's any type. However, v.unknown() is analogous to TypeScript's unknown type and can be used to accept any input value:

const u = v.unknown();

u.parse(1);
// 1

The inverse of v.unknown() is v.never() that fails for every value. This is analogous to TypeScript's never type.

const n = v.never();

n.parse(1);
// ValitaError: invalid_type at . (expected nothing)

By themselves v.unknown() and v.never() are not terribly useful, but they become more relevant with composite types such as Object Types.

Object Types

Validators can be further combined to larger, arbitrarily complex validators. One such combinator is v.object(...), used to check that the input value is an object that has some named properties, and that those properties have a specific type.

const o = v.object({
  company: v.string(),

  // Nested objects work fine too!
  address: v.object({
    city: v.string(),
    country: v.string(),
  }),
});

o.parse({
  name: "Acme Inc.",
  address: { city: "Springfield", country: "Freedomland" },
});
// {
//   name: "Acme Inc.",
//   address: { city: "Springfield", country: "Freedomland" },
// }

o.parse({ name: "Acme Inc." });
// ValitaError: missing_value at .address (missing value)
o.parse({
  name: "Acme Inc.",
  ceo: "Wiley E. Coyote",
  address: { city: "Springfield", country: "Freedomland" },
});
// ValitaError: unrecognized_keys at . (unrecognized key "ceo")

As seen above, unexpected keys like "ceo" are prohibited by default. That default can be changed with Parsing Modes.

Parsing Modes

By default v.object(...) throws an error when it encounters an object with unexpected keys. That behavior can be changed by explicitly passing a parsing mode to .parse(...):

const o = v.object({
  name: v.string(),
});

// Strip away the extra keys
o.parse({ name: "Acme Inc.", ceo: "Wiley E. Coyote" }, { mode: "strip" });
// { name: "Acme Inc." }

// Pass the extra keys through as-is
o.parse({ name: "Acme Inc.", ceo: "Wiley E. Coyote" }, { mode: "passthrough" });
// { name: "Acme Inc.", ceo: "Wiley E. Coyote" }

// Forbid extra keys. This is the default.
o.parse({ name: "Acme Inc.", ceo: "Wiley E. Coyote" }, { mode: "strict" });
// ValitaError: unrecognized_keys at . (unrecognized key "ceo")

The possible values are:

  • { mode: "strict" }: Forbid extra keys. This is the default.
  • { mode: "strip" }: Don't fail on extra keys - instead strip them away from the output object.
  • { mode: "passthrough" }: Just ignore the extra keys and pretend you didn't see them.

The parsing mode applies to all levels of your validation hierarcy, even to nested objects.

const o = v.object({
  company: v.object({
    name: v.string(),
  }),
});

o.parse(
  {
    company: { name: "Acme Inc.", ceo: "Wiley E. Coyote" },
    greeting: "Hello!",
  },
  { mode: "strip" },
);
// { company: { name: "Acme Inc." } }

Rest Properties & Records

Sometimes you may want to allow extra keys in addition to the defined keys. For that you can use .rest(...), and additionally require the extra keys to have a specific type of value:

const o = v
  .object({
    name: v.string(),
    age: v.number(),
  })
  .rest(v.string());

o.parse({ name: "Example McExampleface", age: 42, socks: "yellow" });
// { name: "Example McExampleface", age: 42, socks: "yellow" }

o.parse({ name: "Example McExampleface", age: 42, numberOfDogs: 2 });
// ValitaError: invalid_type at .numberOfDogs (expected string)

The .rest(...) method is also handy for allowing or forbidding extra keys for a specific parts of your object hierarchy, regardless of the parsing mode.

const lenient = v.object({}).rest(v.unknown()); // *Always* allow extra keys
lenient.parse({ socks: "yellow" }, { mode: "strict" });
// { socks: "yellow" }

const strict = v.object({}).rest(v.never()); // *Never* allow extra keys
strict.parse({ socks: "yellow" }, { mode: "strip" });
// ValitaError: invalid_type at .socks (expected nothing)

For always allowing a completely arbitrary number of properties, v.record(...) is shorthand for v.object({}).rest(...). This is analogous to the Record<string, ...> type in TypeScript.

const r = v.record(v.number());

r.parse({ a: 1, b: 2 });
// { a: 1, b: 2 }

r.parse({ a: 1, b: "hello" });
// ValitaError: invalid_type at .b (expected number)

For allowing any collection of properties of any type, v.record() is a shorthand for v.record(v.unknown()).

const r = v.record();

r.parse({ a: 1, b: 2 });
// { a: 1, b: 2 }
r.parse({ a: 1, b: "hello" });
// { a: 1, b: "hello" }

// The input still has to be an object.
r.parse([]);
// ValitaError: invalid_type at . (expected object)

Optional Properties

One common API pattern is that some object fields are optional, i.e. they can be missing completely or be set to undefined. You can allow some keys to be missing by annotating them with .optional().

const person = v.object({
  name: v.string(),
  // Not everyone filled in their theme song
  themeSong: v.string().optional(),
});

person.parse({ name: "Jane Doe", themeSong: "Never gonna give you up" });
// { name: "Jane Doe", themeSong: "Never gonna give you up" }
person.parse({ name: "Jane Doe" });
// { name: "Jane Doe" }
person.parse({ name: "Jane Doe", themeSong: undefined });
// { name: "Jane Doe", themeSong: undefined }

Optionals are only used with v.object(...) and don't work as standalone parsers.

Related Skills

View on GitHub
GitHub Stars421
CategoryDevelopment
Updated5d ago
Forks11

Languages

TypeScript

Security Score

100/100

Audited on Mar 21, 2026

No findings