Runtime validation for static types
—
Complex data structure validators for arrays, objects, tuples, and records. These types allow validation of structured data with specific element and property types.
Validates arrays where all elements conform to a specific element type.
/**
* Creates a validator for arrays of a specific element type
* @param element - Runtype for validating each array element
* @example Array(String).check(["a", "b", "c"]) // ["a", "b", "c"]
* @example Array(Number).check([1, 2, "3"]) // throws ValidationError
*/
function Array<R extends Runtype.Core>(element: R): Array<R>;
interface Array<R extends Runtype.Core = Runtype.Core> extends Runtype<Static<R>[], Parsed<R>[]> {
tag: "array";
element: R;
asReadonly(): Array.Readonly<R>;
}Usage Examples:
import { Array, String, Number, Object } from "runtypes";
// Basic arrays
const StringArray = Array(String);
const NumberArray = Array(Number);
const names = StringArray.check(["Alice", "Bob", "Charlie"]);
const scores = NumberArray.check([95, 87, 92]);
// Nested arrays
const Matrix = Array(Array(Number));
const grid = Matrix.check([[1, 2], [3, 4], [5, 6]]);
// Arrays of objects
const UserArray = Array(Object({
id: Number,
name: String,
active: Boolean
}));
const users = UserArray.check([
{ id: 1, name: "Alice", active: true },
{ id: 2, name: "Bob", active: false }
]);
// Readonly arrays
const ReadonlyNames = Array(String).asReadonly();
type ReadonlyNamesType = Static<typeof ReadonlyNames>; // readonly string[]Validates objects with specific property types and structures.
/**
* Creates a validator for objects with specified property types
* @param fields - Object mapping property names to their runtypes
* @example Object({ name: String, age: Number }).check({ name: "Alice", age: 25 })
*/
function Object<O extends Object.Fields>(fields: O): Object<O>;
interface Object<O extends Object.Fields = Object.Fields> extends Runtype<ObjectStatic<O>, ObjectParsed<O>> {
tag: "object";
fields: O;
isExact: boolean;
asPartial(): Object<PartialFields<O>>;
asReadonly(): Object.Readonly<O>;
pick<K extends keyof O>(...keys: K[]): Object<Pick<O, K>>;
omit<K extends keyof O>(...keys: K[]): Object<Omit<O, K>>;
extend<P extends Object.Fields>(fields: P): Object<O & P>;
exact(): Object<O>;
}
namespace Object {
export type Fields = Record<PropertyKey, Runtype.Core | Optional>;
}Usage Examples:
import { Object, String, Number, Boolean, Optional } from "runtypes";
// Basic object validation
const User = Object({
id: Number,
name: String,
email: String,
active: Boolean
});
const user = User.check({
id: 1,
name: "Alice",
email: "alice@example.com",
active: true
});
// Optional properties
const UserWithOptionals = Object({
id: Number,
name: String,
email: String.optional(),
age: Number.optional(),
bio: String.default("No bio provided")
});
// Object manipulation methods
const PartialUser = User.asPartial(); // All properties optional
const UserNameOnly = User.pick("name", "email"); // Only name and email
const UserWithoutId = User.omit("id"); // All except id
// Extended objects
const AdminUser = User.extend({
role: String,
permissions: Array(String)
});
// Exact objects (reject extra properties)
const StrictUser = User.exact();
StrictUser.check({
id: 1,
name: "Alice",
email: "alice@example.com",
active: true,
extra: "not allowed" // throws ValidationError
});Validates fixed-length arrays with specific types for each position.
/**
* Creates a validator for tuples with specific element types at each position
* @param components - Runtypes for each tuple element in order
* @example Tuple(String, Number).check(["Alice", 25]) // ["Alice", 25]
* @example Tuple(String, Number).check(["Alice"]) // throws ValidationError (wrong length)
*/
function Tuple<T extends readonly Runtype[]>(...components: T): TupleRuntype<T>;
interface TupleRuntype<T> extends Runtype<TupleType<T>> {
tag: "tuple";
components: T;
asReadonly(): ReadonlyTupleRuntype<T>;
}Usage Examples:
import { Tuple, String, Number, Boolean } from "runtypes";
// Basic tuples
const NameAge = Tuple(String, Number);
const person = NameAge.check(["Alice", 25]); // ["Alice", 25]
// Coordinates
const Point2D = Tuple(Number, Number);
const Point3D = Tuple(Number, Number, Number);
const point2d = Point2D.check([10, 20]);
const point3d = Point3D.check([10, 20, 30]);
// Mixed types
const UserRecord = Tuple(Number, String, Boolean);
const record = UserRecord.check([1, "Alice", true]);
// Rest elements (using Spread)
import { Spread } from "runtypes";
const NumbersWithLabel = Tuple(String, Spread(Array(Number)));
const data = NumbersWithLabel.check(["scores", 95, 87, 92, 88]);
// Readonly tuples
const ReadonlyPoint = Point2D.asReadonly();
type ReadonlyPointType = Static<typeof ReadonlyPoint>; // readonly [number, number]Validates objects with dynamic keys but uniform value types.
/**
* Creates a validator for objects with dynamic keys and uniform value types
* @param key - Runtype for validating object keys
* @param value - Runtype for validating all object values
* @example Record(String, Number).check({ a: 1, b: 2 }) // { a: 1, b: 2 }
*/
function Record<K extends PropertyKey, V>(
key: Runtype<K>,
value: Runtype<V>
): RecordRuntype<K, V>;
interface RecordRuntype<K, V> extends Runtype<Record<K, V>> {
tag: "record";
key: Runtype<K>;
value: Runtype<V>;
}Usage Examples:
import { Record, String, Number, Union, Literal } from "runtypes";
// String keys with number values
const StringNumberRecord = Record(String, Number);
const scores = StringNumberRecord.check({
alice: 95,
bob: 87,
charlie: 92
});
// Specific string keys
const AllowedKeys = Union(Literal("red"), Literal("green"), Literal("blue"));
const ColorValues = Record(AllowedKeys, Number);
const colors = ColorValues.check({
red: 255,
green: 128,
blue: 64
});
// Number keys (for array-like objects)
const NumberStringRecord = Record(Number, String);
const arrayLike = NumberStringRecord.check({
0: "first",
1: "second",
2: "third"
});
// Complex value types
const UserProfiles = Record(String, Object({
name: String,
age: Number,
active: Boolean
}));
const profiles = UserProfiles.check({
"user1": { name: "Alice", age: 25, active: true },
"user2": { name: "Bob", age: 30, active: false }
});import { Object, Array, Tuple, Record, String, Number } from "runtypes";
// Deeply nested object
const Company = Object({
name: String,
departments: Array(Object({
name: String,
employees: Array(Object({
id: Number,
name: String,
position: String,
salary: Number
})),
budget: Number
})),
metadata: Record(String, String)
});
// Mixed structure with tuples and arrays
const DataSet = Object({
name: String,
points: Array(Tuple(Number, Number)), // Array of [x, y] coordinates
labels: Record(Number, String), // Index to label mapping
config: Object({
title: String,
axes: Tuple(String, String) // [x-axis label, y-axis label]
})
});import { Object, String, Number, Optional } from "runtypes";
// Explicit optional properties
const UserProfile = Object({
id: Number,
name: String,
email: String,
phone: String.optional(),
avatar: String.optional(),
bio: String.default("No bio available")
});
// Convert existing object to partial
const PartialProfile = UserProfile.asPartial();
// All properties become optional
// Update patterns
const UserUpdate = UserProfile.omit("id").asPartial();
// Remove id, make rest optional for updatesimport { Object, Array, String, Number, type Static } from "runtypes";
const ApiResponse = Object({
data: Array(Object({
id: Number,
title: String,
tags: Array(String)
})),
meta: Object({
total: Number,
page: Number,
hasMore: Boolean
})
});
// Extract TypeScript types
type ApiResponseType = Static<typeof ApiResponse>;
// {
// data: {
// id: number;
// title: string;
// tags: string[];
// }[];
// meta: {
// total: number;
// page: number;
// hasMore: boolean;
// };
// }
// Use in functions
function processApiResponse(response: unknown): ApiResponseType {
return ApiResponse.check(response);
}Install with Tessl CLI
npx tessl i tessl/npm-runtypes