> For the complete documentation index, see [llms.txt](https://radoslaw-medryk.gitbook.io/verifica/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://radoslaw-medryk.gitbook.io/verifica/predicates/custom-predicates.md).

# Custom Predicates

Predicates are functions used by verifica to decide if value is matching some validation rule, and if no, then what is the error. To be valid a `Predicate` , functions need to follow certain format:

```typescript
function isSmallNumber(verificable): VerificaError | VerificaError[] | undefined {
    // perform all necessary checks here,
    
    // return VerificaError or array of those errors if invalid,
    // return undefined/nothing otherwise,
    
    // ot throw VerificaException with provided errors.
}
```

* They need to take one argument which will be `Verificable` object to be checked.
* They need to return: `VerificaError`, an array of `VerificaError` objects, or undefined / nothing.
* They can also throw `VerificaException` with errors, that will be treated the same by verifica as returning `VerificaError` object(s).

## Patterns

In general, fulfilling the requirements specified above is enough for a function to be considered as a `Predicate` . There is a set of useful patterns however, that can make writing `Predicate` functions easier. Those patterns are listed below.

#### Using rawValue() function:

Using `rawValue()` function we can get raw value behind representing it `Verificable` object. We can do our necessary tests directly on it, and then return error / no error based on that.

```typescript
const { rawValue, makeError } = require("verifica");

function isSmallNumber(verificable) {
    // get raw value to be checked:
    const value = rawValue(verificable);
    
    // return error if not matching:
    if (!(typeof value === "number") || value < 0 || value > 10) {
        return makeError(verificable, {
            type: "isSmallNumber",
        });
    }
    
    // return nothing if OK.
}
```

And let's try it out:

```typescript
const { asVerificable, isValid, getErrors } = require("verifica");

const value = 14;
const verificable = asVerificable(value);

isValid(verificable, isSmallNumber); // false
getErrors(verificable, isSmallNumber);
// [{ type: "isSmallnumber", path: [] }]
```

#### Using ensure() function:

Instead of using `rawValue()` , we can also call `ensure()` with some prerequisite condition predicate, like `isNumber` here. If it throws `VerificaException`, the validation will be considered not passed with errors coming from thrown `VerificaException` . If it doesn't throw, we already now `value` is a number, so we only need to check for other custom conditions (like `< 0`, `> 10` in the example) and decide there if error / no error should be returned.

```typescript
const { ensure, isNumber, makeError } = require("verifica");

function isSmallNumber(verificable) {
    // get value ensuring first that it is a number:
    const value = ensure(verificable, isNumber);
    // since ensure didn't throw, we know value is a number here.
    // if ensure would throw VerificaException with errors,
    // it will be treated as returning those error(s).
    
    
    // return error if not matching:
    if (value < 0 || value > 10) {
        return makeError(verificable, {
            type: "isSmallNumber",
        });
    }
    
    // return nothing if OK.
}
```

And let's try it out:

```typescript
const { asVerificable, isValid, getErrors } = require("verifica");

const value = 14;
const verificable = asVerificable(value);

isValid(verificable, isSmallNumber); // false
getErrors(verificable, isSmallNumber);
// [{ type: "isSmallnumber", path: [] }]
```

#### Using multiple ensure() functions:

`ensure()` function can be called multiple times in the same `Predicate` function, e.g. checking if properties of an object matches required conditions as well. If any of those calls throw an `VerificaException`, the whole predicate is considered as not matching, with error(s) taken from the `VerificaException`.

```typescript
const { ensure, isObject, isString, isNumber } = require("verifica");

// We expect object in this format:
// {
//     name: string;
//     address: {
//         street: string;
//         zipCode: number;
//     }
// }

function isUser(verificable) {
    ensure(verificable, isObject);
    ensure(verificable.name, isString);
    
    ensure(verificable.address, isObject);
    ensure(verificable.address.street, isString);
    ensure(verificable.address.zipCode, isNumber);
    
    // If none of above ensure threw an exception, we consider object valid.
    // return nothing if OK.
}
```

And let's try it out:

```typescript
const { asVerificable, isValid, getErrors } = require("verifica");

const value = {
    name: "john Smith",
    address: {
        street: null,
        zipCode: null,
    }
};
const verificable = asVerificable(value);

isValid(verificable, isUser); // false
getErrors(verificable, isUser);
// [{ type: "isString", path: ["address", "street"] }]
```

#### Using VerificaChunk:

`VerificaChunk` can be also used to perform multiple checks on multiple values at the same time.

The difference between using `VerificaChunk` and just multiple `ensure()` calls in a row is that `VerificaChunk` will perform all the checks when `chunk.ensureAll()` is called, and throw `VerificaException` containing all aggregated errors from all the checks. Calling `ensure()` multiple times will throw on first error and only that error will be returned.

```typescript
const { VerificaChunk, isObject, isNumber, isString } = require("verifica");

// We expect object in this format:
// {
//     id: number;
//     name: string;
//     price: number;
// }

function isProduct(verificable) {
    const chunk = new VerificaChunk();
    
    chunk.add(verificable, isObject);
    chunk.add(verificable.id, isNumber);
    chunk.add(verificable.name, isString);
    chunk.add(verificable.price, isNumber);
    
    chunk.ensureAll();
    
    // If chunk.ensureAll() didn't threw an exception, we consider object valid.
    // return nothing if OK.
}
```

And let's try it out:

```typescript
const { asVerificable, isValid, getErrors } = require("verifica");

const value = {
    id: ["a", "b"],
    name: null,
    price: 88,
};
const verificable = asVerificable(value);

isValid(verificable, isProduct); // false
getErrors(verificable, isProduct);
// [
//     { type: "isNumber", path: ["id"] },
//     { type: "isString", path: ["name"] }
// ]
```
