Skip to main content
Every guard in 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.
Once you are in async mode you can continue chaining with the same three methods. You cannot re-enter sync mode after switching.

.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.
If a 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>.
Pass a second argument to override the error message on failure:

Execution order

  1. The sync base guard runs first (is.string.email, etc.)
  2. If the sync guard fails, all async steps are skipped and an error is returned immediately
  3. Any sync transform on the base guard runs (e.g. from .transform() or .trim())
  4. 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 on AsyncGuard