CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-runtypes

Runtime validation for static types

Pending
Overview
Eval results
Files

composite.mddocs/

Composite Types

Complex data structure validators for arrays, objects, tuples, and records. These types allow validation of structured data with specific element and property types.

Capabilities

Array

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[]

Object

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
});

Tuple

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]

Record

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 }
});

Advanced Patterns

Nested Structures

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]
  })
});

Optional and Partial Patterns

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 updates

Type Extraction

import { 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

docs

composite.md

constraints.md

contracts.md

index.md

literals.md

primitives.md

results.md

templates.md

union-intersect.md

utilities.md

validation.md

tile.json