Valita
A typesafe validation & parsing library for TypeScript.
Install / Use
/learn @badrap/ValitaREADME
@badrap/valita

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
- 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.
- Minimalism: Deliver a streamlined and concentrated library that offers just the essentials.
- Extensibility: Allow users to create their own validators and parsers that cater to specific validation scenarios.
Non-Goals
- 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.
- Extensive Built-In Formats: The library does not prioritize having a large array of built-in validation formats out of the box.
- 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 isnull.v.undefined(): Check that the value isundefined.
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
node-connect
337.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
83.3kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
review-duplication
99.2kUse this skill during code reviews to proactively investigate the codebase for duplicated functionality, reinvented wheels, or failure to reuse existing project best practices and shared utilities.
