Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cbrock.dev/llms.txt

Use this file to discover all available pages before exploring further.

Individual guards call .parse() and fail on the first error. Schemas walk the entire structure and collect all errors with full path tracking.

Defining schemas

defineSchemas({ Name: guard })

Pass a map of names to guards. You can use any guard directly, or pass a plain object of guards (auto-wrapped as an object schema).
import { is, defineSchemas } from 'ts-chas/guard';

const schemas = defineSchemas({
  User: is.object({
    id:    is.string.uuid(),
    name:  is.string.min(1).max(100),
    email: is.string.trim().email,
    age:   is.number.int.gte(0).optional,
  }),
  // Plain record — equivalent to wrapping in is.object({ ... })
  Address: {
    street: is.string,
    city:   is.string,
    zip:    is.string.length(5),
  },
});

defineSchema(name, guard)

Creates a single named schema. Useful when you only need one.
import { is, defineSchema } from 'ts-chas/guard';

const UserSchema = defineSchema('User', is.object({
  name: is.string.min(1),
  age:  is.number.int.gte(0),
}));

Parsing and asserting

schema.parse(data)Result<T, GuardErr[]>

Validates data and returns Ok(T) on success or Err(GuardErr[]) containing every validation failure.
const result = schemas.User.parse(incomingData);

if (result.isOk()) {
  console.log(result.value.email); // string — typed
} else {
  for (const e of result.error) {
    console.log(`${e.path.join('.')}: ${e.message}`);
  }
}

schema.assert(data)T

Returns the validated value or throws AggregateGuardError with all collected errors.
try {
  const user = schemas.User.assert(req.body);
  // user is fully typed
} catch (e) {
  if (e instanceof AggregateGuardError) {
    console.log(e.errors);    // GuardErr[]
    console.log(e.format());  // { 'name': ['...'], 'address.zip': ['...'] }
  }
}

schema.is(data)boolean

Boolean type predicate — delegates to the underlying guard.
if (schemas.User.is(value)) {
  value; // typed as User
}

Type inference

InferSchema<typeof schemas.Name>

Extracts the validated output type from a schema.
import type { InferSchema } from 'ts-chas/guard';

type User = InferSchema<typeof schemas.User>;
// { id: string; name: string; email: string; age?: number }

typeof schemas.Name.$infer

Alternative syntax that avoids importing InferSchema.
type User = typeof schemas.User.$infer;

GuardErr

Each error in a Result<T, GuardErr[]> or AggregateGuardError.errors has this shape:
FieldTypeDescription
messagestringHuman-readable description of the failure
pathstring[]Full path from schema root, e.g. ['User', 'address', 'zip']
expectedstringExpected type or constraint, e.g. 'string'
actualstringActual type received, e.g. 'number'
schemastringName of the schema that produced the error
namestringInternal guard name, e.g. 'string.email'
import type { GuardErr } from 'ts-chas/guard';

const result = schemas.User.parse({ id: 'bad', name: '', email: 123 });
if (result.isErr()) {
  const errors: GuardErr[] = result.error;
  // errors[0].path     → ['User', 'id']
  // errors[0].message  → 'Value "bad" failed validation'
  // errors[0].expected → 'string'
}

AggregateGuardError

Thrown by schema.assert() when validation fails.
MemberTypeDescription
errorsGuardErr[]All collected validation errors
schemaNamestringName of the schema
format()() => Record<string, string[]>Returns a path-keyed map of messages
flatten()() => { path: string; message: string }[]Returns a flat array of { path, message }
import { AggregateGuardError } from 'ts-chas/guard';

try {
  schemas.User.assert(badData);
} catch (e) {
  if (e instanceof AggregateGuardError) {
    e.format();
    // {
    //   'name':        ['Value "" failed validation'],
    //   'address.zip': ['Expected string, but got number (12345)'],
    // }

    e.flatten();
    // [
    //   { path: 'name',        message: 'Value "" failed validation' },
    //   { path: 'address.zip', message: 'Expected string, but got number (12345)' },
    // ]
  }
}

formatErrors and flattenErrors

Standalone utilities for formatting GuardErr[] arrays outside of AggregateGuardError.
import { formatErrors, flattenErrors } from 'ts-chas/guard';

const result = schemas.User.parse(data);
if (result.isErr()) {
  formatErrors(result.error);
  // { 'email': ['Expected string, but got number (123)'] }

  flattenErrors(result.error);
  // [{ path: 'email', message: 'Expected string, but got number (123)' }]
}
The schema name prefix is stripped from all paths. Root-level errors (no nested path) use the key '_root'.

Complete example

import { is, defineSchemas, formatErrors, type InferSchema } from 'ts-chas/guard';

// 1. Define schemas
const schemas = defineSchemas({
  CreateUserRequest: is.object({
    name:     is.string.trim().min(1).max(100),
    email:    is.string.trim().toLowerCase().email.max(255),
    age:      is.number.int.between(0, 150).optional,
    role:     is.literal('admin', 'editor', 'viewer'),
    tags:     is.array(is.string.min(1)).max(10),
    address:  is.object({
      street: is.string.min(1),
      city:   is.string.min(1),
      zip:    is.string.regex(/^\d{5}(-\d{4})?$/),
    }),
  }),
});

// 2. Infer the type
type CreateUserRequest = InferSchema<typeof schemas.CreateUserRequest>;

// 3. Parse incoming data
function handleCreateUser(body: unknown) {
  const result = schemas.CreateUserRequest.parse(body);

  if (result.isErr()) {
    // Return structured validation errors to the client
    return {
      status: 400,
      errors: formatErrors(result.error),
      // {
      //   'email':          ['Expected string, but got number (123)'],
      //   'role':           ['Value "superuser" failed validation'],
      //   'address.zip':    ['Value "abc" failed validation'],
      // }
    };
  }

  const user = result.value;
  // user.email is string — already trimmed and lowercased
  // user.role  is 'admin' | 'editor' | 'viewer'
  return { status: 201, user };
}

// 4. Use assert when you want exceptions (e.g. in trusted internal code)
import { AggregateGuardError } from 'ts-chas/guard';

function mustBeValidUser(data: unknown): CreateUserRequest {
  try {
    return schemas.CreateUserRequest.assert(data);
  } catch (e) {
    if (e instanceof AggregateGuardError) {
      throw new Error(`Invalid user data: ${e.message}`);
    }
    throw e;
  }
}