> ## 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.

# Guard

> Chainable runtime type validation.

A `Guard<T>` is a TypeScript type predicate; a function `(value: unknown) => value is T` — with chainable helpers for common validations, schema parsing, and transformation. Unlike most validators, guards work as plain `if` conditions, so narrowing is free.

```typescript theme={null}
import { is } from 'ts-chas/guard';

declare const value: unknown;

if (is.string(value)) {
  value; // narrowed to string here
}
```

## The `is` namespace

All built-in guards live on the `is` object:

```typescript theme={null}
import { is } from 'ts-chas/guard';

is.string    // Guard<string>
is.number    // Guard<number>
is.boolean   // Guard<boolean>
is.bigint    // Guard<bigint>
is.symbol    // Guard<symbol>
is.null      // Guard<null>
is.undefined // Guard<undefined>
is.object({...}) // Guard<{ ... }>
is.array(guard)  // Guard<T[]>
```

## Chaining helpers

Under the hood, `ts-chas` chains evaluations from left to right. When you use the guard as a boolean type predicate:

```typescript theme={null}
if (is.string.trim().email(value)) {
// ...
}
```

Here is exactly what happens during the `if` check:

1. It validates that **value** is a `string` (from `is.string`).
2. It applies the `.trim()` inline to a temporary, internal value.
3. It passes that temporary, trimmed string into the `.email(...)` validation rule.

If all three steps succeed, the predicate returns `true`, and TypeScript safely narrows **value** to a `string`.

It's important to remember that because TypeScript type predicates (`v is string`) *cannot mutate variables*, the value inside the `if` block is still the original, \_un-trimmed \_string you started with. It's simply \_guaranteed \_to be an email once you trim it!

If you want the actual trimmed string value to work with, you map it through `.parse(...)` or `.assert(...)` instead of using it as a raw boolean check:

```typescript theme={null}
// Safely returns the fully trimmed/transformed email!
const result = is.string.trim().email.parse(value); 

if (result.isOk()) {
console.log(result.value); // "contact@example.com"
}
```

## Universal helpers

Every guard, regardless of type, has these methods:

### `.parse(value)` → `Result<T, GuardErr>`

The primary way to validate data and get a typed result:

```typescript theme={null}
const result = is.string.email.parse(rawInput);
// Result<string, GuardErr>

if (result.isOk()) {
  sendEmail(result.value); // string, already validated
}
```

### `.error(message)` — custom error messages

```typescript theme={null}
const validAge = is.number.gte(18).error('Must be 18 or older');
const result = validAge.parse(15);
// Err({ message: 'Must be 18 or older', ... })

// Optional context to generate dynamic error messages
const validEmail = is.string.email.error(({ meta, value }) => `${meta.name}: expected email, got ${JSON.stringify(value}`);
```

### `.nullable()`, `.optional()`, `.nullish()`

```typescript theme={null}
is.string.nullable  // Guard<string | null>
is.string.optional  // Guard<string | undefined>
is.string.nullish   // Guard<string | null | undefined>
```

### `.and()`, `.or()`

```typescript theme={null}
const stringOrNumber = is.string.or(is.number);  // Guard<string | number>
const richObject = is.object({ name: is.string })
  .and(is.object({ age: is.number }));            // Guard<{ name: string } & { age: number }>
```

### `.where(predicate)` — arbitrary refinement

```typescript theme={null}
const evenNumber = is.number.int.where((n) => n % 2 === 0);
```

### `.brand(tag)` — branded types

```typescript theme={null}
const UserId = is.string.uuid('v4').brand('UserId');
type UserId = typeof UserId.$infer; // string & { __brand: 'UserId' }
```

## Object guards

```typescript theme={null}
const isUser = is.object({
  name: is.string.min(1),
  age: is.number.gte(18),
  email: is.string.email,
});

if (isUser(rawData)) {
  rawData.name; // string
  rawData.age;  // number
}
```

## Array guards

```typescript theme={null}
const isTags = is.array(is.string).min(1).max(10);
const isUsers = is.array(isUser);

if (isTags(data.tags)) {
  data.tags; // string[]
}
```

The `.array` property also works as a shorthand on any guard:

```typescript theme={null}
const isStringArray = is.string.array; // Guard<string[]>
```

## Schema parsing

Use `defineSchemas` to define reusable schemas that collect *all* validation errors instead of short-circuiting on the first:

```typescript theme={null}
import { defineSchemas, type InferSchema } from 'ts-chas/guard';

const schemas = defineSchemas({
  User: {
    id: is.string.uuid('v4'),
    email: is.string.trim().email,
    age: is.number.gte(18).error('Must be an adult'),
    tags: is.array(is.string).min(1),
  },
});
```

### `InferSchema` — type inference

```typescript theme={null}
type User = InferSchema<typeof schemas.User>;
// { id: string; email: string; age: number; tags: string[] }

// Or use the $infer shorthand:
type User = typeof schemas.User.$infer;
```

### `.parse(data)` → `Result<T, GuardErr[]>`

```typescript theme={null}
const result = schemas.User.parse(incomingData);

if (result.isOk()) {
  saveUser(result.value); // typed as User
} else {
  console.error(result.error.map((e) => e.message));
  // ['Expected string, got number (123)', 'Must be an adult']
}
```

### `.assert(data)` — throw on failure

```typescript theme={null}
import { AggregateGuardError } from 'ts-chas/guard';

try {
  schemas.User.assert(incomingData);
} catch (e) {
  if (e instanceof AggregateGuardError) {
    console.error(e.format());
    // { 'User.email': ['Expected email, got "notanemail"'], ... }
  }
}
```

## Standard Schema v1 compatibility

All guards implement the [Standard Schema v1](https://github.com/standard-schema/standard-schema) spec via the `~standard` property. This means you can drop them directly into tRPC, react-hook-form, Drizzle, and other compatible ecosystems with no adapter needed:

```typescript theme={null}
// tRPC
const procedure = t.procedure.input(isUser);

// react-hook-form
const { register } = useForm({ resolver: standardSchemaResolver(isUser) });
```

## Namespace extensions

Add your own guards to the `is` namespace with `is.extend()`. Extensions are typed and chainable:

```typescript theme={null}
import { is } from 'ts-chas/guard';

const myIs = is.extend({
  positiveEven: (v: unknown): v is number =>
    is.number.positive(v) && is.number.even(v),
});

myIs.positiveEven(4); // true
myIs.positiveEven(3); // false
```

<Tip>
  Store the extended `myIs` instance as your project's canonical import. This lets you share custom guards across your codebase without re-extending everywhere.
</Tip>

## Hashing with `is.string.hash()`

The `is.string.hash()` helper uses [@noble/hashes](https://github.com/paulmillr/noble-hashes) under the hood, which runs isomorphically in Node.js, browsers, and edge runtimes with no native bindings required:

```typescript theme={null}
// Validate a SHA-256 hex hash string
const isSha256Hex = is.string.hash({ alg: 'sha256', enc: 'hex' });
isSha256Hex(someHashString); // true if valid length and format

// Timing-safe verification against known plaintext
const verifyPassword = is.string.hash({ alg: 'sha256', enc: 'hex' }).verify('mysecret');
verifyPassword.parse(storedHash); // Ok(string) if storedHash matches sha256('mysecret')
```

Supported algorithms: `sha1`, `sha256`, `sha384`, `sha512`, `md5`. Supported encodings: `hex`, `base64`, `base64url`.
