Usage
While prose is fine and all, examples are a much better way to get a good overview of Vality.
With Vality, you only have to write each model once. You can then use it for validation and get the benefits of type checking for free.
Models
Get started with writing a model definition for your schema.
ts
import {v } from "vality";constUser = () =>({username :v .string ,} asconst );
ts
import {v } from "vality";constUser = () =>({username :v .string ,} asconst );
A model is really just a function that returns an object. Don't forget to return the object as const
or certain features of Vality will not work correctly.
You can then use this object to define how your data should look, by specifying the properties it ought to have and their respective types. For example, to make sure that a property email
is always an email, add the following property to the object: email: v.email
. Don't forget that objects may be nested arbitrarily deep:
ts
constUser = () =>({username :v .string ,contact : {v .string ,phone :v .number ,discord : {username :v .string ,discriminator :v .number ,},},} asconst );
ts
constUser = () =>({username :v .string ,contact : {v .string ,phone :v .number ,discord : {username :v .string ,discriminator :v .number ,},},} asconst );
Type
The real beauty of Vality is that you write your validation model and type definition in one place. Use the Parse<T>
type to extract this information and use it to define your model type definition.
All you have to do is to pass typeof Model
to the Parse
type and it will return a type that represents the model type definition.
ts
typeUserModel =Parse <typeofUser >;
ts
typeUserModel =Parse <typeofUser >;
The typeof
keyword is necessary because the model itself is written in JavaScript and needs to be converted to a type first.
ts
type'Person' refers to a value, but is being used as a type here. Did you mean 'typeof Person'?2749'Person' refers to a value, but is being used as a type here. Did you mean 'typeof Person'?PersonModel =Parse <>; Person
ts
type'Person' refers to a value, but is being used as a type here. Did you mean 'typeof Person'?2749'Person' refers to a value, but is being used as a type here. Did you mean 'typeof Person'?PersonModel =Parse <>; Person
Guards
To specify the type of a property, Vality offers a number of guards.
Guards, such as v.string
and v.number
, are used to check for atomar data types, and can easily be extended to check for more complex data. See the full documentation for a list of all built-in guards.
Options
To further specify the type of a property, you can additionally pass options to guards, which is done by calling it with an object.
ts
constUser = () =>({age :v .number ({min : 18 }), // Will only accept numbers that are at least 18realName :v .string ({m }), // We even get autocomplete!} asconst );
ts
constUser = () =>({age :v .number ({min : 18 }), // Will only accept numbers that are at least 18realName :v .string ({m }), // We even get autocomplete!} asconst );
Options have no influence on the type of the guard when processed by Parse
.
Valits
More complex data structures, such as objects or arrays, are represented by valits.
Similar to guards, there are a number of built-in valits (see the full documentation for a complete list), and they too are extendable. However, unline guards, valits take arguments that determine their type. For example, v.array
takes a single guard and then checks for an array of the passed guard's type.
ts
constUser = () =>({username :v .string ,// (additional properties omitted for brevity)languages :v .array (v .string ),} asconst );typeUserModel =Parse <typeofUser >;
ts
constUser = () =>({username :v .string ,// (additional properties omitted for brevity)languages :v .array (v .string ),} asconst );typeUserModel =Parse <typeofUser >;
Options
Valits can also be refined with options, which are passed by calling them with an object after specifying the guard. Basically by calling them twice (also called currying).
ts
constUser = () =>({luckyNumbers :v .array (v .number )({minLength : 3,maxLength : 5 }),} asconst );
ts
constUser = () =>({luckyNumbers :v .array (v .number )({minLength : 3,maxLength : 5 }),} asconst );
Options have no influence on the type of the valit when processed by Parse
.
Shorthands (Shorts)
Another very handy feature of Vality are shorthands for certain valits. For example, an array with only one element is treated the same way as v.array
. A list of these shorts can be found in the full documentation.
ts
constUser = () =>({// Same as v.array(v.enum(v.literal("de"), v.literal("en"), v.literal("sv"), v.literal("fr")))languages : [["de", "en", "sv", "fr"]],} asconst );typeUserModel =Parse <typeofUser >;
ts
constUser = () =>({// Same as v.array(v.enum(v.literal("de"), v.literal("en"), v.literal("sv"), v.literal("fr")))languages : [["de", "en", "sv", "fr"]],} asconst );typeUserModel =Parse <typeofUser >;
Options
It is not possible to provide options to shorts. If you need to provide extra constraints, you have to pass those to the verbose version. For example, [v.number]( ... )
is not valid, rather would you have to call v.array(v.number)( ... )
.
Enys
Eny is a general term for everything that Vality can deal with. This includes guards, valits and shorthands, which all can be used for valits, for example. As valits take enys as arguments, and valits themselves are enys, this allows for arbitrarily deep nesting and complex data structures.
ts
constPerson = () =>({name :v .string ,address : {street :v .optional (v .string ),city :v .optional (v .string ),country :v .string ,},} asconst );constManufacturer = () =>({name :v .string ,ceo :Person ,cars : [Car ],} asconst );constCar = () =>({manufacturer :Manufacturer ,horsepower :v .number ({min : 1,}),fuel : ["petrol", "diesel", "electric"],} asconst );// Hover the typesdeclare typePersonModel =Parse <typeofPerson >;declare typeManufacturerModel =Parse <typeofManufacturer >;declare typeCarModel =Parse <typeofCar >;
ts
constPerson = () =>({name :v .string ,address : {street :v .optional (v .string ),city :v .optional (v .string ),country :v .string ,},} asconst );constManufacturer = () =>({name :v .string ,ceo :Person ,cars : [Car ],} asconst );constCar = () =>({manufacturer :Manufacturer ,horsepower :v .number ({min : 1,}),fuel : ["petrol", "diesel", "electric"],} asconst );// Hover the typesdeclare typePersonModel =Parse <typeofPerson >;declare typeManufacturerModel =Parse <typeofManufacturer >;declare typeCarModel =Parse <typeofCar >;