ts-chas runs synchronously by default. When a validation step requires I/O (a database lookup, an API call, a file read) you need async mode. Calling .whereAsync(), .refineAsync(), or .transformAsync() on any guard switches it into async mode and returns an AsyncGuard<T>.
AsyncGuard is not a Guard. It does not have .parse(), .assert(), or the helper chain. Its sole output method is .parseAsync(), which returns a ResultAsync<T, GuardErr>.
Entering async mode
Any of the three async methods can start the chain. You can add sync helpers to the base guard first — those run before any async step..whereAsync(fn)
Appends an async predicate. Resolving false fails validation. The current value is passed to fn and must pass for the chain to continue.
whereAsync step returns false, subsequent steps are not called:
.refineAsync(fn)
Appends an async same-type transformation. The resolved value replaces the current value and is passed to subsequent steps. The type parameter stays T.
.transformAsync(fn)
Appends an async type-changing transformation. The return type of fn becomes the new T of the returned AsyncGuard<U>.
Chaining steps in order
All three methods can be mixed freely. Steps execute in declaration order. Each step receives the value produced by the step before it..parseAsync(value, errMsg?)
The only way to run an AsyncGuard. Returns a ResultAsync<T, GuardErr>.
Execution order
- The sync base guard runs first (
is.string.email, etc.) - If the sync guard fails, all async steps are skipped and an error is returned immediately
- Any sync transform on the base guard runs (e.g. from
.transform()or.trim()) - Async steps execute in declaration order
ResultAsync API
.parseAsync() returns a ResultAsync<T, GuardErr>, which is a promise-like with a full monadic API. You can chain operations without awaiting intermediate steps:
.unwrap() resolves to the value on success and rejects on failure:
Accessing .meta
AsyncGuard exposes the .meta of the underlying sync guard, which is useful for introspection and error reporting:
Common patterns
Database uniqueness check
Fetch and validate an external resource
Normalize then re-validate
Composing with sync transforms
Sync transforms on the base guard are applied before any async step. This means you can use.transform() (or helpers like .trim()) to pre-process the value synchronously, then do the async work on the clean result:
Summary
Key behaviors to remember:
- Sync guard runs first; async steps are skipped entirely on sync failure
- Steps execute in declaration order; each step receives the value from the previous step
.parseAsync()is the only output method — there is no.assert()or predicate equivalent onAsyncGuard