struct-js
v1.1.5
Published
`struct.js` is a military-grade validation library, with focus on fluent type definition and precise error reporting.
Downloads
4
Readme
Welcome
struct.js
is a military-grade validation library, with focus on fluent type
definition and precise error reporting.
npm install struct-js
Terminology
A validator
is a Function
that returns either exactly true
or the precise
description
of all errors.
For simple values, the description
is a String
. For containers, the
description
is a mapping of keys to their individual error description
.
function equalsOne(object) {
return (object === 1) ? true : `Expected 1, not ${object}`
}
A struct
is an object where each key is:
- A built-in type (
String
,Number
,Boolean
, orDate
) - A
validator
function (equalsOne
) - An array with a
struct
as its only element ([ String ]
) - An object with a
struct
as its values ({ name: String }
)
const userStruct = {
name: {
first: String,
last: String
},
height: Number,
birth: Date,
flags: [ Boolean ]
}
Validate
validate(object, struct)
is the generic validator
. It will verify that an object
matches a struct
, and return either true
or a description
.
import { validate } from 'struct-js'
validate("hello", String) // true
validate(123, String) // "This should be of type String, not Number"
Let's see struct.validate
handle an invalid userStruct
:
let invalidUser = {
name: {
first: false,
last: 1234
},
height: new Date(),
birth: NaN,
flags: [ true, 'not a flag', true ]
}
validate(invalidUser, userStruct)
{
name: {
first: 'This should be of type String, not Boolean',
last: 'This should be of type String, not Number'
},
height: 'This should be of type Number, not Date',
birth: 'This should be of type Date, not Number',
flags: { '1': 'This should be of type Boolean, not String' }
}
validate()
has two cousins:
isValid()
returnstrue
orfalse
:isValid('string', String) // true isValid(1, String) // false
requireValid()
throws aValidationError
if nottrue
:try { requireValid(1, String) } catch(ve) { console.log(ve.details) // the validation result console.log(ve.message) console.log(ve.stack) }
Advanced Validators
The recommended pattern for creating advanced validators
is to use a factory
,
a function that given parameters returns a validator
. For example, a more
generic version of equalsOne
:
function equals(number) {
return (object) =>
(object === number) ? true : `Expected ${number}, not ${object}`
}
struct.js
alredy comes with some useful factories
. import
them from the
module.
optional
optional(struct)
allows an object to be null
or undefined
, validating
it against struct
if not.
const struct = {
unimportant: optional(String)
}
validate({ unimportant: 'nothing' }, struct) // true
validate({ unimportant: null }, struct) // true
validate({}, struct) // true
instanceOf
instanceOf(Class)
verifies an object is an insance of a Class
.
class X {}
const struct = {
'x': instanceOf(X)
}
validate({ x: new X() }, struct) // true
validate({ x: 'what?' }, struct)
{ x: 'This should be of type X, not String' }
oneOf
oneOf(...structs)
validates an object against many structs
, returning true
if any validation is successful, an array of error descriptions
otherwise.
const struct = {
secretCode: oneOf(String, Number)
}
validate({ secretCode: 123 }, struct) // true
validate({ secretCode: null }, struct)
{
secretCode: [
'This should be of type String, not null',
'This should be of type Number, not null'
]
}
allOf
allOf(...structs)
validates an object against many structs
, returning true
only if all validations are successful, an array of error descriptions
otherwise.
const struct = {
name: allOf(String, minLength(4))
}
validate({ name: 'John' }, struct) // true
validate({ name: '!' }, struct)
{
name: [ 'This should be have length >= 4, not 1' ]
}
inEnum
inEnum(validValues)
ensures an object is found in an array of validValues
.
const struct = {
status: inEnum([ 'draft', 'sent', 'deleted' ])
}
validate({ status: 'draft' }, struct) // true
validate({ status: 'other' }, struct)
{
status: 'This should be one of [draft,sent,deleted], not other'
}
mapOf
mapOf(struct)
ensures an object is a mapping of keys to struct
values, returning
true
if every own property passes validation, or a map of keys to errors
if not.
const struct = mapOf({ name: String })
validate({ bob: { name: "Bob" } }, struct) // true
validate({ bob: { foo: 1 } }, struct)
{
"bob": {
"name": "This should be of type String, not undefined",
"foo": "This property should not be present"
}
}
Usage patterns
struct.js
can be extremely effective at controlling code complexity and ensuring
correctness. Here are some patterns to explore.
Named structs
Assign names to structs
, and compose them into complex type definitions:
const userStruct = {
name: String,
age : Number
}
const groupStruct = {
leader : userStruct,
members: [ userStruct ]
}