README
Gubu: An object shape validation utility.
Quick Example | Common Use Cases | Install and Usage | Shape Rules | API
This is a schema validator in the tradition of Joi or any JSON-Schema validator, with the key features:
- Schemas are WYSIWYG - you define a schema with a template exactly matching your object structure;
const optionValidator = Gubu({
port: 8080, // port must be a number; if not defined, it will be 8080
host: 'localhost' // host must be a string; if not defined, it will be 'localhost'
})
- The most useful cases are the easiest to specify (e.g. optional defaults are just literal values);
- The implementation is iterative (a depth-first loop over the property tree) not recursive, so it's nice and fast and can handle any size of data and schema.
Why write yet another validator? I've used Joi
for a long time, but
always found its schema definition a little verbose at the syntax
level. I've never liked JSON-Schema - it's just too noisy to
eyeball. What I do like is Vue.js property
validation,
but that only works at the top level. I did write a prototype deep
Vue property validator using
Joi, but it's pretty clunky.
This validator is motivated by two use cases: adding message validation to the Seneca microservices framework, and providing deep defaults for complex custom Vue.js components. I think it does both jobs rather nicely with an easy-to-read syntax.
This open source module is sponsored and supported by Voxgig. |
---|
Quick Example
const { Gubu } = require('gubu')
// Property a is optional, must be a Number, and defaults to 1.
// Property b is required, and must be a String.
const shape = Gubu({ a: 1, b: String })
// Object shape is good! Prints `{ a: 99, b: 'foo' }`
console.log( shape({ a: 99, b: 'foo' }) )
// Object shape is also good. Prints `{ a: 1, b: 'foo' }`
console.log( shape({ b: 'foo' }) )
// Object shape is bad. Throws an exception with message:
// Validation failed for path "a" with value "BAD" because the value is not of type number.
// Validation failed for path "b" with value "" because the value is required.'
console.log( shape({ a: 'BAD' }) )
As shown above, you use the exported Gubu
function to create a
validation checker (does the argument match the schema shape?). If
valid, the checker returns its first argument, otherwise it throws an
exception listing all (not just the first!) the validity errors.
Common Use Cases
Option defaults and validation
Let's say you have a server that needs to run on a given host and port, but by default should run on localhost on port 8080. The host should be a non-empty string, and the port should be a number.
const optionShape = Gubu({
host: 'localhost',
port: 8080
})
// These print: { host: 'localhost', port: 8080 }
console.log(optionShape())
console.log(optionShape({}))
// Prints: { host: 'localhost', port: 9090 }
console.log(optionShape({ port: 9090 }))
// All of these throw an error.
console.log(optionShape({ host: 9090 })) // Not a string.
console.log(optionShape({ port: '9090' })) // Not a number.
console.log(optionShape({ host: '' })) // Not really a usable string!
Deep structures
You're building a front end component that displays complex data from the back end, and you want to handle missing data gracefully, at any depth in the structure.
const productListShape = Gubu({
products: [
{
name: String, // Product name is a required String
img: 'generic.png' // Use a default image if not defined
}
]
})
let result = productListShape({})
// No products, but our data structure still has an array where
// one is expected, so no `undefined` errors.
result === {
products: []
}
// Fix data with missing fields
let result = productListShape({
products: [
{ name: 'Apple', img: 'apple.png' },
{ name: 'Pear', img: 'pear.png' },
{ name: 'Banana' } // Missing image!
]
})
// Banana will not have a broken image.
result === {
products: [
{ name: 'Apple', img: 'apple.png' },
{ name: 'Pear', img: 'pear.png' },
{ name: 'Banana', img: 'generic.png' }
]
}
Shape Building
For more specific shapes, such as required objects and arrays, you can use shape builder functions that still respect your data structure.
The Required makes a value explicitly required:
const userShape = Gubu({
person: Required({ // person must be an object
name: String,
age: Number,
})
})
// FAILS: 'Validation failed for path "person" with value "" because the value is required.')
userShape({})
// This will pass, returning the object:
userShape({
person: {
name: 'Alice',
age: 99
}
})
For the full list of shape builders, see the API reference.
Install
$ npm install gubu
Usage
The Gubu module has no dependencies. A single function named Gubu
is exported. Utility functions are provided as properties of Gubu
or can be exported separately.
TypeScript
Gubu is written in TypeScript, and can be imported naturally:
import { Gubu } from 'gubu'
Types are provided in gubu.d.ts.
Gubu tries to play nice with compile-time type validation of your shapes, and mostly succeeds.
Browser
A minified version is provided as gubu.min.js, which
can be directly loaded into a web page and exports a Gubu
global
object.
However you're probably better off importing this module in the usual manner for your build process and bundling it together with everything else.
Shape Rules
The general principle of Gubu's design is that the schema shape should match a valid object or value as closely as possible.
For scalar values you can provide a native type object to make the value required:
Gubu(String)
matches strings:'foo'
Gubu(Number)
matches numbers:123
Gubu(Boolean)
matches booleans:true
Or defaults to make the value optional:
Gubu('bar')
matches strings:'foo'
, andundefined
Gubu(0)
matches numbers:123
, andundefined
Gubu(false)
matches booleans:true
, andundefined
If a value is optional and undefined
, the default value is returned:
Gubu('bar')()
returns 'bar'
.
The values null
and NaN
must match exactly. The value undefined
is special - it literally means no value.
Empty strings are not considered to be valid if a string is required
(this is usually what you want). To allow empty string, use
Gubu(Empty(String))
(where Empty
is exported by the Gubu
module).
For objects, write them as you want them:
let shape = Gubu({
foo: {
bar: {
zed: String,
qaz: Number,
}
}
})
The above shape will match:
{
foo: {
bar: {
zed: 'x',
qaz: 1
}
}
}
For arrays, the first element is treated as the shape that all elements in the array must match:
Gubu([String])
matches['a', 'b', 'c']
Gubu([{x:1}])
matches[{x: 11}, {x: 22}, {x: 33}]
If you need specific elements to match specific shapes, add these shapes after the first element:
Gubu([String,Number])
matches[1, 'b', 'c']
- as the first element (of the shape) is aNumber
, and the general element isString
.
Thus, the element 0
of a shape array defines the general element,
and following elements define special cases (offset by 1).
You can specify custom validation using functions:
Gubu({a: (v) => 10<v })
: matches{a: 11}
as10 < 11
And you can manipulate the value if you need to:
Gubu({a: (v,u) => 10<v ? (u.val=2*v, true) : false })
: matches{a: 11}
as10 < 11
and returns{a: 22}
.
You can also compose validations together:
const shape = Gubu({ a: Gubu({ x: Number }) })
// Matches { a: { x: 1 } } as expected
shape({ a: { x: 1 } })
Gubu exports shape "builder" utility functions that let you further
refine the shape (You've already seen the Empty
builder above that
allows strings to be empty). You wrap your value with the builder
function to apply the desired effect.
The Required
builder makes a value required:
const { Gubu, Required } = require(`gubu`)
const shape = Gubu({
a: Required({x: 1}) // Property `a` is required and must match `{x: 1}`.
})
The Closed
builder prohibits an object from having additional unspecified properties:
const { Gubu, Closed } = require(`gubu`)
// Only properties `a` and `b` are allowed.
const shape = Gubu(Closed({
a: 1,
b: true
}))
You can also access builders as properties of the main Gubu
function, and you can also chain most builders. Thus a Required
and
Closed
object can be specified with:
const { Gubu } = require(`gubu`)
const shape = Gubu({
a: Gubu.Closed({ x: 1 }).Required(),
b: Gubu.Required({ x: 1 }).Closed(), // Also works.
})
You can also write your own builders - see the Shape Builders section.
In addition to this README, the unit tests are comprehensive and provide many usage examples.
Shape Builders
The built-in shape builders help you match the following shapes:
- Required or optional:
- Value constraints:
- Empty: Allow string values to be empty.
- Exact: The value must match one of an exact list of values.
- All: All shapes must match value.
- Some: Some shapes (at least one) must match value.
- One: Exactly one shape (and no more) must match value.
- Any: This shape will match any value.
- Never: This shape will never match anything.
- Length constraints (operate on values with a length or numeric value):
- Below: Match a value (or length of value) less than the given amount.
- Max: Match a value (or length of value) less than or equal to the given amount.
- Min: Match a value (or length of value) greater than or equal to the given amount.
- Above: Match a value (or length of value) greater than the given amount.
- General constraints:
- Mutations:
- Customizations:
Recursive Shapes
You can use the Define and Refer
shape builders to validate recursive shapes. Use Define
first to
name a given shape. Then use Refer
to apply the definition of the shape.
let tree = Gubu({
root: Define('BRANCH', {
value: String,
left: Refer('BRANCH'),
right: Refer('BRANCH'),
})
})
// This passes, returning the object.
tree({
root: {
value: 'A',
left: {
value: 'AB',
left: {
value: 'ABC'
},
right: {
value: 'ABD'
},
},
right: {
value: 'AE',
left: {
value: 'AEF'
},
},
}
})
// This fails with error:
// "Validation failed for path "root.left.left.left.value" with value "123" because the value is not of type string."
tree({
root: {
value: 'A',
left: {
value: 'AB',
left: {
value: 'ABC',
left: {
value: 123
},
},
},
}
})
API
Shape
API | Shapes | Errors | Custom Validation | Builders
A shape specification can either be at the root of a JSON structure, an array element, or the value of an object property. For a value to pass validation, it is compared to the shape, and must match the constraints of the shape. If the shape has elements or properties, these must also match, and are validated recursively in a depth-first manner [^1].
Required Scalars
The value must be of the indicated type, and must exist.
String
: match any string, but not the empty string [^2].Number
: match any number, but notBigInt
values.Boolean
: match any boolean.Symbol
: match any symbol.BigInt
: match anyBigInt
(including the1n
syntax form).Date
: match an object created withnew Date(...)
RegExp
: match an object created with/.../
ornew RegExp(...)
Optional Scalars with Defaults
The value must be of the indicated type, and is derived from the given default. If the value does not exist, the default value is inserted.
foo
: match any string, but replace an empty string [^3].123
: match any number, but notBigInt
values.true
: match any boolean.new Symbol('bar')
: match any symbol.new BigInt(456)
: match anyBigInt
(including the1n
syntax form).new Date()
: match an object created withnew Date(...)
/x/
: match an object created with/.../
ornew RegExp(...)
Objects
Plain objects can be specified directly as they appear in values to be
validated. If you want an object {foo: 123}
, then the shape is also
{foo: 123}
, meaning any object with a foo
property that is a
number
. The foo
property is optional, and will default to the
value 123
.
You can define plain objects to any depth. The shape { bar: { foo: 123} }
defines an object that optionally contains another object as
the value of the property bar
.
As objects and sub-objects are often referenced directly in data structures, Gubu will construct missing objects by default, and fill in the missing child values (which may themselves be objects).
The general form of an object shape is:
{
'propName0': <SHAPE>,
'propName1': <SHAPE>,
...
'propNameN': <SHAPE>,
}
where propNameX
is any string (the quotes may be omitted if the
property name is a valid JavaScript identifier—this is just
normal JavaScript syntax after all).
The <SHAPE>
can be any valid Gubu shape definition.
Required Properties (Object)
To mark an object property as required, use the required
scalar shapes (such as String
), or use the shape
builder Required:
const { Required } = Gubu
let shape = Gubu({
foo: Number,
bar: Required({
zed: Boolean
})
})
// These pass, returning the value unchanged.
shape({ foo: 1, bar: { zed: false } })
shape({ foo: 1, bar: { zed: false, baz: 2 }, qaz: 3 }) // new properties are allowed
// These fail, throwing an Error.
shape({ bar: { zed: false } }) // foo is required
shape({ foo: 'abc', bar: { zed: false } }) // foo is not a number
shape({ foo: 1 }) // bar is required
shape({ foo: 1, bar: {} }) // bar.zed is required
Object properties that are required must always be provided, even if they are children of optional objects—they wouldn't be required otherwise! To allow such deep required properties to be missing, use an explicit Optional shape builder:
let strictShape = Gubu({ a: { b: String } })
// Passes
strictShape({ a: { b: 'ABC' } })
// Fails, even though a is not required.
strictShape({})
let easyShape = Gubu({ a: Optional({ b: String }) })
// Now both pass
easyShape({ a: { b: 'ABC' } })
easyShape({})
// This still fails, as `a` is now defined, and needs `b`
easyShape({ a: {} })
Closed Objects
To restrict the set of allowed properties, use the shape builder Closed:
const { Closed } = Gubu
let shape = Gubu(Closed({
a: Closed({ x: 1 }),
b: { y: 2 }
}))
// These pass, returning the value with defaults inserted
shape({ a: { x: 11 }, b: { y: 22 } })
shape({ a: { x: 11 } }) // b is optional, returns { a: { x: 11 }, b: { y: 2 } }
shape({}) // a is optional, returns { a: { x: 1 }, b: { y: 2 } }
// These fail, throwing an Error.
shape({ a: { x: 11 }, b: { y: 22 }, c: { z: 33 } }) // c is not allowed
shape({ a: { x: 11, k: 44 } }) // k is not allowed inside { x: 11 }
If a property must be present in an object, used the shape builder Required.
Optional Objects
Objects are optional by default, and will be created if not present. To prevent this, use the explicit shape builder Optional to indicate that you do not wish an object to be inserted when it is missing.
const { Optional } = Gubu
let shape = Gubu({
a: { x: 1 },
b: Optional({ y: 2 }),
c: Optional({ z: Optional({ k: 3 }) }),
})
// Explicitly optional properties are not inserted as defaults if missing.
shape({}) // returns { a: { x: 1 } }, b and c are missing entirely
shape({ b: {} }) // returns { a: { x: 1 }, b: { y: 2 } }, defaults for b are inserted
shape({ c: {} }) // returns { a: { x: 1 }, c: {} }, c has no non-optional defaults
shape({ c: { z: {} } }) // returns { a: { x: 1 }, c: { z: { k: 3 } } } // z has defaults
If the object value is present but empty, any default values will be inserted.
Object Values
You can define a general shape for all non-explicit object values using the shape builder Value:
const { Value } = Gubu
let shape = Gubu(Value({
a: 123,
}, String))
// All new properties must be a String
shape({ a: 11, b: 'abc' }) // b is a string
shape({ c: 'foo', d: 'bar' }) // c and d are strings
// These fail
shape({ a: 'abc' }) // a must be a number
shape({ b: { x: 1 } }) // b must be a string
The general shape can be any valid shape:
const { Required, Value } = Gubu
let shape = Gubu({
people: Required({}).Value({ name: String, age: Number })
})
// This passes:
shape({ people: {
alice: { name: 'Alice', age:99 },
bob: { name: 'Bob', age:98 },
} })
// These fail:
shape({ people: {
alice: { name: 'Alice', age:99 },
bob: { name: 'Bob' }, // age is a required number
} })
shape({}) // people is a required object
Arrays
Arrays can be specified directly using the first element as the shape
that each element of the value array must match. If you want an array
of numbers ([ 1, 2, 3 ]
, say), then the shape is [Number]
.
You can also define special shapes for individual elements at specific indexes, as shown below.
As arrays are often referenced directly in data structures, Gubu will construct missing arrays by default, and fill in the missing element values if there are empty array entries.
The general form of an array shape is:
[
<SHAPE>,
<SPECIAL-SHAPE-0>,
<SPECIAL-SHAPE-1>,
...
<SPECIAL-SHAPE-N>,
]
where <SHAPE>
is any valid Gubu shape definition. All elements
must match <SHAPE>
, unless they are special cases.
For the special cases, they correspond to the first, second, etc elements of the value array (and are thus offset by +1 in the array shape):
let shape = Gubu([Number,String,Boolean])
// These pass, returning the array as is.
shape([ 'abc', true ])
shape([ 'abc', true, 123 ])
shape([ 'abc', true, 123, 456 ])
// These fail.
shape([ 123 ]) // Index 0 must be a string
shape([ 'abc', 123 ]) // Index 1 must be a boolean
Required Properties (Array)
To mark an array element as required, use the required
scalar shapes (such as String
), or use the shape
builder Required.
Only required special elements must be present. Empty arrays with a
required general element shape (such as [Number]
), can still be empty
([]
with match). Element constraints apply to elements, not the
array itself.
let shape = Gubu([ { x: 1 }, Required({ y: Boolean }) ])
// These pass:
shape([ { y: true } ]) // Index 0 is special
shape([ { y: false }, { x: 123 } ]) // Index >= 1 must match general element shape
// These fail:
shape([]) // Index 0 is required
shape([ { x: 123 } ]) // Index 0 is special
Length Constraints
You can control the allowed length of an array using the shape builder Never to make all elements special, and also by using Min, Max, Above, and Below to restrict the length of the array.
Use the shape builder Never as the first element to prevent additional elements in the array:
const { Never } = Gubu
Gubu([Never()]) // Only accepts the empty array []
Gubu([Never(),String]) // Only accepts an array with a string first element ['abc']
The length constraining shape builders (Min, Max, Above, and Below) work in a sensible way depending on the data type. For strings they control character length, for numbers they control magnitude, for object they control key count, and for arrays, they control length:
let { Min } = Gubu
let shape = Gubu(Min(2, [Number]))
// These pass
shape([11,22]) // length is 2, >= minimum 2
shape([11,22,33]) // length is 3, >= minimum 2
// These fail
shape([11]) // length is 1, not >= minimum 2
shape([]) // length is 0, not >= minimum 2
Array Properties
JavaScript allows arrays to have properties: let a = []; a.foo = 1
.
Matching against array properties is not supported in the current
version. The workaround is write a custom
validator.
Functions
To require a function, use the shape Function
:
Gubu(Function)(()=>true)
will pass.
To provide a default function, you'll need to create a shape manually,
using the special G$
utility. Literal functions are used as custom
validators, as this is the most common use case.
let { G$ } = Gubu
let shape = Gubu({ fn: G$({ v: ()=>true }) })
shape({}) // returns { fn: ()=>true }
shape({ fn: ()=>false }) // returns { fn: ()=>false }
Custom Validation
API | Shapes | Errors | Custom Validation | Builders
You can define custom validators by providing a function as the
shape. The first argument to this function will the value to
validate. Return true
if the value is valid, and false
if not.
let shape = Gubu({ a: (v) => 10 < v })
shape({ a: 11 }) // passes, as 10 < 11 is true
shape({ a: 9 }) // fails, as 10 < 9 is false
You modify the value using the second argument (Update
) to the custom
validation function:
let shape = Gubu({ a: (value, update) => {
update.val = value * 2
return true
})
shape({ a: 3 }) // returns { a: 6 }
As a special case, if you want to explicitly set the value to
undefined
or NaN
, use the property uval
.
You can also provide a custom error message using the update
argument:
let shape = Gubu({ a: (value, update) => {
update.err = 'BAD VALUE $VALUE AT $PATH'
return false // always fails
})
shape({ a: 3 }) // throws "BAD VALUE 3 AT a"
The special replacement tags $VALUE
and $PATH
are replaced with
the value, and the path to the value, respectively.
The third argument (State
) to a custom validator is the internal state of the
validation process. This provided for special cases and workarounds,
and the internal set of properties is not stable. Review the source
code to see
what is available.
let shape = Gubu({ a: (value, update, state) => {
update.val = value + ` KEY=${state.key}`
})
shape({ a: 3 }) // returns { a: '3 KEY=a'}
Gubu function
To construct a shape use the Gubu
function exported by this module:
// Using require
const { Gubu } = require(`gubu`)
// Using import
import { Gubu } from 'gubu'
let shape = Gubu({ x: 1 })
In the browser, Gubu adds itself directly to the window
object for
immediate use, if you directly load this module using a script
tag. However you'll probably just want to import Gubu in the usual
way and let your package builder look after things.
The Gubu
function has arguments:
shape
(optional): any valid shape definition ('abc'
,String
,{ x: 123 }
, etc.).options
(optional): an options object.
The shape argument can be anything, and is used to define the validation shape expected. See the sections above for descriptions of the various shapes.
The options are:
name
: a string defining a custom name for this shape (useful for debugging).
The Gubu
function provides all built-in shape builders
(Required
, Closed
, etc.) as properties. These are also exported directly
from the module, so the following are equivalent:
const { Gubu, Required } = import 'gubu'
const { Gubu } = require('gubu')
const { Require } = Gubu
If you are concerned about namespacing the builders (if the names clash with your own names), use a 'G' prefix as an alias:
Gubu.GRequired = Gubu.Required
GubuShape function
When you create a shape using Gubu
, a GubuShape
shape validator
function is returned:
// TypeScript
import { Gubu, GubuShape } from 'gubu'
// GubuShape is inferred:
const shape = Gubu(123) // shape is a generic GubuShape
The shape validator function has arguments:
value
: the value to validate (and modify with defaults).context
: (optional) a context object containing your own data.
The value can be anything. It is not duplicated and will be mutated if defaults are inserted.
The context is a general purpose store for anything you might want to use in custom validation builders. It may also be used by builders to hold state information (the name of the builder is used for namespacing).
The context does have reserved names:
err
: an array of validation errors
If you provide a context with the property err
as an empty array,
any validation errors will be added to this array, and an Error will
not be thrown:
let ctx = { err: [] }
Gubu(Number)('abc', ctx) // does not throw
console.log(err[0]) // prints error description (number was expected)
The error descriptions are plain objects, not Errors.
The GubuShape
function has the following methods:
valid(value: any, context?: any): boolean
:true
if the shape matches (can be used as type guard in TypeScript),match(value: any, context?: any): any
: Same asGubuShape
, but does not mutate the value, and can be used as type guard in TypeScripttoString()
: returns a short string describing thisGubuShape
instance[Util.inspect.custom]()
: same astoString
spec()
: returns a declarative description of the shape
The shape description provided by spec
can be passed to Gubu
to
generate a new separate shape instance.
Many shapes can be fully serialized to JSON, but those with custom validator function are not serializable in the current version.
A GubuShape
can be used be used as part of new shape
definition. They are intended to be composable.
Shape Nodes
The data structure returned by GubuShape.spec
is the internal
representation of the validation shape. This is a hierarchical data
structure where the validation for each key-value pair is defined by a
shape Node
, which has the following structure:
$
: typeof GUBU : Special marker to indicate normalized.t
: ValType : Value type name.d
: number : Depth.v
: any : Default value.r
: boolean : Value is required.o
: boolean : Value is explicitly optional.u
: Record<string, any> : Custom user meta datab
: Validate[] : Custom before validation functions.a
: Validate[] : Custom after validation functions.
The ValType
is string with exactly one of these values:
'any'
: Any type.'array'
: An array.'bigint'
: A BigInt value.'boolean'
: The valuestrue
orfalse
.'custom'
: Custom type defined by a validation function.'function'
: A function.'instance'
: An instance of a constructed object.'list'
: A list of types under a given logical rule.'nan'
: TheNaN
value.'never'
: No type.'null'
: Thenull
value.'number'
: A number.'object'
: A plain object.'string'
: A string (but not the empty string).'symbol'
: A symbol reference.'undefined'
: Theundefined
value.
This structure is deliberately terse to make eye-balling deep structure print-outs easier.
As noted above, in the current version this structure is only fully serializable to JSON if there are no custom validations, and the custom user meta data is serializable.
This structure can be accessed in custom
validators via the state
parameter, and in
shape builders via the before and
after hook functions. It is also provided in error
messages under the n
property.
Errors
API | Shapes | Errors | Custom Validation | Builders
Gubu will attempt a full validation of the input value, collect all
errors, and throw an Error if any validation failed. The error object
will be an instance of GubuError
, which extends TypeError
with the
following extra properties:
gubu
: A marker value, alwaystrue
.code
: Top level error code, in this version, always'shape'
desc()
: Call this function to get a more detailed description of the error.
The error description returned by desc()
has the properties:
name
: Always'GubuError'
code
: Top level error code, in this version, always'shape'
err
: An array ofErrDesc
objects (these are all the errors that occurred).ctx
: The context object (if any) passed to thisGubuShape
validation call.
The message string of GubuError is a human readable generic
description of the validation failure (with one issue per line) that
is usable in your own application as-is. You can use the ErrDesc
object instead to create entirely custom messages. Custom messages for
specific custom errors can also be defined (see below).
Gubu(Number)('abc') // throws an Error with message:
'Validation failed for path "" with value "x" because the value is not of type number.'
Gubu({ top: { foo: String, bar: Number }})({ top: { foo: 123, bar: 'abc'}}) // throws an Error with message:
`
Validation failed for path "top.foo" with value "123" because the value is not of type string.
Validation failed for path "top.bar" with value "abc" because the value is not of type number.
`
ErrDesc Object
The ErrDesc
object is the internal representation of an error,
containing the full details of the error, which you can use for
customization. The properties are:
k: string
: Key of failing value.n: Node
: Failing shape node.v: any
: Failing value.p: string
: Key path to value.w: string
: Error code ("why").m: number
: Error mark for debugging.t: string
: Error message text.u: any
: User custom info.
The property n
is the shape node whose validation failed.
try {
Gubu({x: Number})({x: 'abc'})
}
catch(gubuError) {
gubuError.desc()) === {
name: 'GubuError',
code: 'shape',
err: [
{
k: undefined,
n: {
'