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.
is.object({ ... })
Validates that a value is a plain object whose fields satisfy the provided shape. Fields not listed in the shape are allowed by default.
import { is } from 'ts-chas/guard';
const PersonGuard = is.object({
name: is.string.min(1),
age: is.number.int.gte(0),
});
PersonGuard({ name: 'Alice', age: 30 }); // true
PersonGuard({ name: 'Alice', age: 30, role: 'admin' }); // true — extra key allowed
PersonGuard({ name: '', age: 30 }); // false
Helpers
.strict (Transformer Property) — disallows extra keys not listed in the shape.
const Strict = is.object({ id: is.number }).strict;
Strict({ id: 1, extra: true }); // false
.partial() (Transformer Factory) — makes all fields optional (T | undefined). If an array of keys is passed as argument, only those keys become optional.
const Patch = is.object({ name: is.string, age: is.number }).partial();
Patch({}); // true, all fields are now optional
Patch({ name: 'x' }); // true
.required() (Transformer Factory) — makes all fields required. If an array of keys is passed as argument, only those keys become required.
const Patch = is.object({ name: is.string, age: is.number }).required();
Patch({ name: 'Chase', age: 999 }); // true, all fields are now required
Patch({ name: 'x' }); // false
.pick([keys]) (Transformer Factory) — keeps only the listed keys.
const IdOnly = is.object({ id: is.number, name: is.string }).pick(['id']);
IdOnly({ id: 1 }); // true
IdOnly({ id: 1, name: 'x' }); // false — name is an extra key in the narrowed shape
.omit([keys]) (Transformer Factory) — removes the listed keys from the shape.
const NoPassword = is.object({ email: is.string, password: is.string }).omit(['password']);
NoPassword({ email: 'a@b.co' }); // true
.extend({ ... }) (Transformer Factory) — merges additional fields into the shape. Returns a new guard. Prefer this over the top-level is.and() for merging objects.
const Base = is.object({ id: is.number });
const Named = Base.extend({ name: is.string });
Named({ id: 1, name: 'Alice' }); // true
.catchall(innerGuard) (Transformer Factory) — allows any extra keys not defined in the schema, but only if they satisfy the provided guard.
const Base = is.object({ id: is.number });
const Named = Base.extend({ name: is.string });
Named({ id: 1, name: 'Alice' }); // true
Other object helpers
| Helper | Guard Type | Validates |
|---|
.size(n) | Factory | Size = n |
.minSize(n) | Factory | Size ≥ n |
.maxSize(n) | Factory | Size ≤ n |
.has(key, guard?) | Transformer Factory | Object has property that (optionally) satisfies guard |
.hasAll(keys) | Factory | Object has all specified keys |
.hasOnly(keys) | Factory | Object has only the specified keys |
is.array(guard?)
Validates that a value is an array. Pass one or more element guards to validate every item.
is.array()([1, 'two', true]); // true — unknown[]
is.array(is.number)([1, 2, 3]); // true — number[]
is.array(is.string, is.number)(['a', 1]); // true — (string | number)[]
Helpers
| Helper | Guard Type | Validates |
|---|
.min(n) | Factory | Length ≥ n |
.max(n) | Factory | Length ≤ n |
.length(n) | Factory | Length = n |
.nonEmpty | Property | Length ≥ 1 |
.unique | Property | Every element is unique |
.includes(item) | Factory | Array contains item |
.excludes(item) | Factory | Array does not contain item |
.readonly | Transformer Property | Wraps array in Object.freeze(), returns readonly array when parsed |
const tags = is.array(is.string).nonEmpty.max(10);
tags.parse(['typescript', 'validation']); // Ok(string[])
tags.parse([]); // Err(...)
is.record(keyGuard, valueGuard)
Validates that a value is a plain object where every key and value satisfies the provided guards.
When keyGuard has a finite set of values (is.enum or is.literal), the record performs exhaustive key checking — all keys must be present and no extra keys are allowed. This matches TypeScript’s Record<'a' | 'b', V> semantics.
When keyGuard is open-ended (is.string, is.number), it validates that every existing key/value pair satisfies the guards without requiring any specific keys.
// Open-ended — any string key is fine
const scores = is.record(is.string, is.number);
scores({ alice: 90, bob: 85 }); // true
scores({ alice: 'A' }); // false — value must be number
// Exhaustive — all keys must be present, no extras
const Flags = is.record(is.literal('darkMode', 'beta'), is.boolean);
Flags({ darkMode: true, beta: false }); // true
Flags({ darkMode: true }); // false — 'beta' is missing
is.record has all of the same helpers as is.object
is.tuple([...], restGuard?)
Validates a fixed-length array where each position has its own guard. Pass a second argument to allow variadic trailing elements.
// Fixed length
const Point = is.tuple([is.number, is.number]);
Point([10, 20]); // true
Point([10, 20, 30]); // false — too many elements
// With rest elements
const Args = is.tuple([is.string], is.number);
Args(['cmd']); // true — rest is optional
Args(['cmd', 1, 2, 3]); // true — [string, ...number[]]
Args(['cmd', 'nope']); // false — rest element must be number
| Helper | Guard Type | Validates |
|---|
.nonEmpty | Property | Tuple is not empty |
.unique | Property | All elements are unique |
.min(n) | Factory | Length ≥ n |
.max(n) | Factory | Length ≤ n |
.size(n) | Factory | Length = n |
.includes(item) | Factory | Tuple has item |
.excludes(item) | Factory | Tuple does not have item |
.readonly | Transformer Property | Tuples are already readonly, included for consistency |
is.union(...), is.intersection(...), is.xor(...)
is.union(...guards) — passes if the value satisfies at least one guard.
const StringOrNumber = is.union(is.string, is.number, is.boolean);
StringOrNumber('hello'); // true — string | number | boolean
StringOrNumber(null); // false
is.intersection(...guards) — passes if the value satisfies all guards.
const Named = is.object({ name: is.string });
const Aged = is.object({ age: is.number });
const Person = is.intersection(Named, Aged);
Person({ name: 'Alice', age: 30 }); // true — { name: string } & { age: number }
Person({ name: 'Alice' }); // false
is.xor(...guards) — passes if the value satisfies exactly one guard.
const XorGuard = is.xor(
is.object({ a: is.string }),
is.object({ b: is.number }),
);
XorGuard({ a: 'hello' }); // true — matches first only
XorGuard({ a: 'hello', b: 1 }); // false — matches both
Built-in type guards
is.date
Validates a Date instance.
const pastDate = is.date.before(new Date());
pastDate.parse(new Date('2020-01-01')); // Ok(Date)
const normalized = is.date.startOf('year');
normalized.parse(new Date('2025-06-15')); // Ok(2025-01-01T00:00:00.000Z)
| Helper | Guard Type | Validates |
|---|
.before(date) | Factory | Date is before the given date |
.after(date) | Factory | Date is after the given date |
.between(min, max) | Factory | Date is between min and max |
.weekend | Property | Date is a weekend |
.weekday | Property | Date is a weekday |
.day(dow) | Factory | Date is given day of the week |
.month(num) | Factory | Date has given month (0-indexed) |
.year(num) | Factory | Date has given year |
.dayOfMonth(num) | Factory | Date is a specific day of the month (1-31) |
.timezoneOffset(minutes) | Factory | Date has given timezone offset |
.hour(num) | Factory | Date has specific hour |
.minute(num) | Factory | Date has specific minute |
.second(num) | Factory | Date has specific second |
.millisecond(num) | Factory | Date has specific millisecond |
.sameDayAs(date) | Factory | Date is on the same calendar day as given date |
.sameMonthAs(date) | Factory | Date is in the same month as given date |
.sameYearAs(date) | Factory | Date is in the same year as given date |
.future | Property | Date is in the future |
.past | Property | Date is in the past |
.today | Property | Date is today |
.tomorrow | Property | Date is tomorrow |
.yesterday | Property | Date is yesterday |
.startOf(unit) | Transformer Factory | Truncates to start of 'year', 'month', 'day', etc. |
.endOf(unit) | Transformer Factory | Same as .startOf but for end of unit |
is.regexp
Validates a RegExp instance. Optionally validates against a specific pattern.
is.regexp(/hello/); // true
is.regexp('hello'); // false
const specific = is.regexp(/^[a-z]+$/);
specific.parse(/^[a-z]+$/); // Ok(RegExp)
| Helper | Guard Type | Validates |
|---|
.global | Property | Has a g flag |
.ignoreCase | Property | Has i flag |
.multiline | Property | Has m flag |
.unicode | Property | Has u flag |
.sticky | Property | Has y flag |
.dotAll | Property | Has s flag |
.source(pattern) | Factory | Pattern matches source |
.test(string) | Factory | String passes value.test() |
.flags(string) | Factory | Has exactly the specified flags |
is.url
Validates a URL instance (not a string — use is.string.url for strings).
is.url(new URL('https://example.com')); // true
is.url('https://example.com'); // false
const toHref = is.url.transform(u => u.href);
toHref.parse(new URL('https://example.com')); // Ok('https://example.com/')
| Helper | Guard Type | Validates |
|---|
.http | Transformer Property | Protocol is http or https |
.https | Transformer Property | Protocol is strictly https |
.secure | Transformer Property | Same as .https |
.local | Transformer Property | Matches localhost, 127.0.0.1, ::1 |
.hasSearch | Transformer Property | Has a search query string |
.hasHash | Transformer Property | Has a hash fragment |
.protocol(pattern) | Factory | Pattern matches protocol WITHOUT trailing colon |
.hostname(pattern) | Factory | Pattern matches hostname |
.pathname(pattern) | Factory | Pattern matches pathname |
.port(n?) | Factory | Port = n or is present |
is.map(keyGuard?, valueGuard?)
Validates a Map instance with optional key and value guards.
is.map()(new Map()); // true
is.map(is.string, is.number)(new Map([['a', 1]])); // true
is.map(is.string, is.number).nonEmpty(new Map([['a', 1]])); // true
is.map().size(2)(new Map([['a', 1], ['b', 2]])); // true
| Helper | Guard Type | Validates |
|---|
.nonEmpty | Property | Size > 0 |
.empty | Property | Size = 0 |
.size(num) | Factory | Size = num |
.minSize(num) | Factory | Size ≥ num |
.maxSize(num) | Factory | Size ≤ num |
.hasKey(key) | Factory | Contains key |
.hasValue(value) | Factory | Contains value |
.readonly | Transformer Property | Is readonly, narrows to Readonly<Map<T>> |
is.set(valueGuard?)
Validates a Set instance with an optional value guard.
is.set()(new Set()); // true
is.set(is.number)(new Set([1, 2, 3])); // true
is.set(is.string).nonEmpty(new Set(['a'])); // true
is.set(is.number).subsetOf([1, 2, 3, 4])(new Set([1, 2])); // true
| Helper | Guard Type | Validates |
|---|
.nonEmpty | Property | Size > 0 |
.empty | Property | Size = 0 |
.size(num) | Factory | Size = num |
.minSize(num) | Factory | Size ≥ num |
.maxSize(num) | Factory | Size ≤ num |
.has(value) | Factory | Contains value |
.subsetOf(superset) | Factory | Is subset of iterable superset |
.supersetOf(subset) | Factory | Is superset of iterable subset |
.disjointFrom(other) | Factory | Is disjoint from other |
.readonly | Transformer Property | Is readonly, narrows to Readonly<Set<T>> |
is.promise
Validates any thenable (has a .then method). There are very few use-cases for this.
is.promise(Promise.resolve(42)); // true
is.promise(42); // false
is.error
Validates an Error instance.
| Helper | Guard Type | Validates |
|---|
.message(str) | Factory | Error message contains str |
.name(str) | Factory | Error name equals str |
.hasCause | Property | Error cause ≠ undefined |
is.error(new Error('oops')); // true
is.error.message('oops')(new Error('oops')); // true
is.error.name('TypeError')(new TypeError('bad')); // true
is.file
Validates a File or Blob instance.
| Helper | Guard Type | Validates |
|---|
.type(mimeType) | Factory | MIME type matches |
.extension(ext or ext[]) | Factory | Specific file extension(s) |
size(bytes) | Factory | File size = bytes |
.minSize(bytes) | Factory | File size ≤ bytes |
.maxSize(bytes) | Factory | File size ≤ bytes |
.name(pattern) | Factory | Is File object & filename matches |
.lastModified(date or number) | Factory | Is File object & last modified date |
const imageFile = is.file.type('image/png').maxSize(5 * 1024 * 1024);
imageFile.parse(uploadedFile); // Ok(File) or Err(...)
Custom and chas-specific guards
is.custom(fn)
Creates a guard from any predicate function. Use a TypeScript type predicate for full narrowing, or pass a generic type parameter.
const isOdd = is.custom((n): n is number => typeof n === 'number' && n % 2 !== 0);
isOdd(3); // true
isOdd(2); // false
const isEven = is.custom<number>(n => typeof n === 'number' && n % 2 === 0);
isEven(4); // true
is.result()
Guards a Result<T, E> value from the ts-chas result module.
import { ok, err } from 'ts-chas/result';
is.result(is.string, is.error); // Guard<Result<string, Error>>
is.result()(ok(42)); // true
is.result(is.number)(ok(42)); // true
is.result().ok(is.number)(ok(42)); // true — narrows to Ok variant
is.result().err(is.string)(err('fail')); // true — narrows to Err variant
| Helper | Guard Type | Validates |
|---|
.err(innerGuard?) | Transformer Factory | Is Result::Err and error passes innerGuard, narrows to Result<T, InferGuard<typeof innerGuard>> |
.ok(innerGuard?) | Transformer Factory | Is Result::Ok and value passes innerGuard, narrows to
Result<InferGuard<typeof innerGuard>, E> |
is.option()
Guards an Option<T> value.
import { some, none } from 'ts-chas/option';
is.option()(some(42)); // true
is.option(is.number)(some(42)); // true
is.option().some(is.number)(some(42)); // true
is.option().none(none()); // true
| Helper | Guard Type | Validates |
|---|
.some(innerGuard?) | Transformer Factory | Is Option::Some , value != null and passes innerGuard, narrows to Some<T> |
.none | Transformer Property | Is Option::None, narrows to None |
is.tagged(factory)
Guards tagged errors created with defineErrs.
import { defineErrs } from 'ts-chas/tagged-errs';
const AppError = defineErrs({
NotFound: (id: string) => ({ id }),
Forbidden: (reason: string) => ({ reason }),
});
is.tagged(AppError.NotFound)(error); // narrows to NotFoundErr — typed id available
is.tagged(AppError)(error); // narrows to NotFoundErr | ForbiddenErr
is.tagged('NotFound')(error); // structural match on _tag