Documentation index
Russian translation
This page is a compact contract summary for AI agents, code generators, IDE tools, and human readers who want the shortest possible set of stable rules.
Treat it as the "canonical quick reference" layer above the longer guides.
@modulify/validator is organized into three runtime layers:
- predicates: runtime checks and type guards;
- assertions: machine-readable leaf failures;
- combinators: structural composition over assertions and validators.
The library does not center itself around built-in human-readable messages.
Violations are structured data first.
Use the root package for:
validatevalidate.syncmatches.syncmetadescribecustomcollection- built-in assertions
- combinators
Use @modulify/validator/predicates for standalone guard-style runtime checks.
Use @modulify/validator/json-schema for:
toJsonSchema(...)JsonSchemaExportError
type ValidationTuple<T> =
| [ok: true, validated: T, violations: []]
| [ok: false, validated: unknown, violations: Violation[]]Important consequences:
validate(...)narrows thevalidatedtuple item in the success branch;validate(...)does not narrow the original input variable;matches.sync(...)is the API that narrows the original variable;violationsis always empty on success.
optional(x)acceptsundefinednullable(x)acceptsnullnullish(x)acceptsnull | undefined
These wrappers model runtime acceptance, not UI wording.
shape(...) validates plain record-like objects.
Defaults:
- unknown keys are allowed;
- unknown-key mode is
'passthrough'; - object-level rules are empty.
Mode switches:
.strict()keeps the same fields and rules, but rejects unknown keys;.passthrough()keeps the same fields and rules, but allows unknown keys.
Structural derivations:
.pick(...).omit(...).partial(...).extend(...).merge(...)
Important rule:
- structural derivations intentionally drop object-level rules;
- mode switches intentionally keep object-level rules.
Violations are machine-readable objects with:
- failed value;
- path;
- semantic subject in
violates.
Do not depend on text parsing for downstream processing.
Prefer:
violationscollection(...)- path-based lookups
Over:
- string matching;
- ad-hoc message parsing;
- field-name extraction from text.
meta(...) attaches opaque machine-readable metadata to any constraint.
describe(...) returns a stable recursive descriptor tree.
Custom validators may participate in this contract by exposing describe().
Without describe(), a custom validator remains opaque and usually appears as:
{ kind: 'validator' }toJsonSchema(...) is a derived interoperability layer.
It is not the source of runtime truth.
Best-effort mode:
- default mode;
- unsupported nodes become permissive
{}schemas; - unsupported shape rules may be dropped with
$comment.
Strict mode:
- throws
JsonSchemaExportError; - exposes
descriptor,reason, andpath.
Important mismatch:
- JSON Schema
requiredis only an approximation of runtimeundefinedsemantics; - do not assume perfect semantic parity between runtime validation and exported JSON Schema.
Use this when you want to validate a payload:
const [ok, validated, violations] = validate.sync(input, schema)Use this when you want to narrow the original variable:
if (matches.sync(value, schema)) {
// value is narrowed here
}Use this when you want reusable object schemas:
const schema = shape({...}).strict()
const partial = schema.partial()
const subset = schema.pick([...])Use this when another layer needs machine-readable introspection:
const descriptor = describe(schema)Use this when another system needs an export view:
const jsonSchema = toJsonSchema(schema)Do:
- keep leaf constraints small and composable;
- treat
violationsas data, not messages; - use
shape(...)for reusable object contracts; - add
describe()to custom validators that should participate in tooling; - use strict JSON Schema export only when lossy export is unacceptable.
Do not:
- expect
validate(...)to narrow the original input binding; - assume
toJsonSchema(...)is a full mirror of runtime semantics; - assume structural shape derivations keep object-level rules;
- rely on undocumented private internals instead of
describe(...); - build downstream logic around human-readable strings.
For implementation details and edge cases, prefer this order:
README.mddocs/en/*.mdtests/*.test.tstests/*.test-d.ts
Tests are the most precise source for behavior that is easy to misunderstand from prose alone.