0.0.0 • Published 7 months ago
navapi v0.0.0
Expressive Validation for Objects.
An object validation library with a strong TypeScript support.
Installation
npm install xvo
Usage
import { x } from 'xvo';
// Declare
const schema = x.union(
x.object({
account_type: x.literal('individual'),
first_name: x.string(),
last_name: x.string(),
phone_number: x.string().phone(),
email: x.string().email(),
nickname: x.string().optional(),
email_verified: x.boolean().default(false)
}),
x.object({
account_type: x.literal('business'),
business_name: x.string(),
phone_number: x.string().phone(),
email: x.string().email(),
alias: x.string().optional(),
email_verified: x.boolean().default(false)
})
);
// Parse
const result = schema.parse({
account_type: 'individual',
first_name: 'John',
last_name: 'Doe',
phone_number: '+1 (123) 456-7890',
email_verified: 'user@example.com'
});
// result: { ok: boolean }
if (!result.ok) {
// result: { ok: false, error: XvoError }
throw new Error(x.error_to_string(result.error));
}
// result: { ok: true, value: object }
const value = result.value;
console.log(value);
{
"account_type": "individual",
"first_name": "John",
"last_name": "Doe",
"phone_number": "+11234567890",
"email": "user@example.com",
"verified": false
}
if (value.account_type === 'individual') {
console.log("Welcome back, " + value.first_name + "!");
}
"Welcome back, John!"
Custom Error-Handling
if (!result.ok) {
// result: { ok: false, error: XvoError }
const error = result.error;
switch (error.type) {
'missing_required':
// error: { type: 'missing_required', field: string }
break;
default:
// 'invalid_type' |
// 'invalid_range' |
// 'invalid_pattern' |
// 'invalid_format' |
// 'invalid_literal' |
// 'custom', 'unknown' ...
break;
}
}
RFC-compliant Normalization
(Default - On) Receive RFC-compliant version of the value, as long as it is a valid phone number.
x.string().phone().parse('+1 (123) 456-7890');
// { ok: true, value: '+11234567890'; }
(Off) Receive value as-is, as long as it is a valid phone number.
x.string().phone(false).parse('+1 (123) 456-7890');
// { ok: true, value: '+1 (123) 456-7890'; }
This does not affect the validation of the fields—both are valid. It only affects the received value on the result object.
No redundant method-chaining
// 🚫 Invalid: Mixing optional and default doesn't make logical sense.
x.string().optional().default("example").optional(); // invalid
x.string().optional().default("example"); // invalid
// ✅ Valid: Choose one approach—`default()` inherently makes the field optional.
x.string().default("example"); // valid
Cleaner Type Errors for Easier Developement
Example with comparison to other validation libraries
Setting .default(null)
without .nullable()
.
In Zod: z.string().default(null)
-> Type Error.
No overload matches this call.
Overload 1 of 2, '(def: string): ZodDefault<ZodString>', gave the following error.
Argument of type 'null' is not assignable to parameter of type 'string'.
Overload 2 of 2, '(def: () => string): ZodDefault<ZodString>', gave the following error.
Argument of type 'null' is not assignable to parameter of type '() => string'.
In Xvo: x.string().default(null)
-> Type Error.
Argument of type 'null' is not assignable to parameter of type 'string'.
0.0.0
7 months ago