Structures
Structural validators orchestrate nested validation pipelines and maintain proper issue paths for complex data shapes.
object(shape, message?)
Validates an object with specified properties. Unknown keys are allowed by default.
Parameters:
shape: Object mapping keys to schemas. Wrap schema in[]for optional properties.message: Custom error message or handler
Issue Codes:
'object:expected_object': Value is not an object- Plus any issues from nested property validators
const user = v.object({
id: v.string(),
name: v.string()
.toTrimmed(),
age: [v.number()
.min(0)], // Optional property
})
user.execute({ id: '123', name: ' Alice ' })
// { value: { id: '123', name: 'Alice', age: undefined } }strictObject(shape, message?)
Like object() but rejects unknown keys.
const strict = v.strictObject({
id: v.string(),
})
strict.execute({ id: '123', extra: 'not allowed' })
// { issues: [{ code: 'object:unknown_key', ... }] }looseObject(shape, message?)
Alias for object(). Explicitly allows unknown keys while validating declared properties.
array(elementSchema, message?)
Validates each element of an array with the provided schema.
Issue Codes:
'array:expected_array': Value is not an array- Plus any issues from element validators (with index in path)
const tags = v.array(v.string()
.toLowercase())
.min(1)
.max(5)
tags.execute(['JS', 'TS', 'NODE'])
// { value: ['js', 'ts', 'node'] }
tags.execute(['a', 123, 'c'])
// { issues: [{ path: [1], code: 'string:expected_string', ... }] }Chainable Methods:
min(count)- Minimum array lengthmax(count)- Maximum array lengthtoFiltered(predicate)- Filter elementstoSorted(compareFn?)- Sort arraytoSliced(start, end?)- Slice arraytoLength()- Replace array with its length (number)
union(schemas, message?)
Tries each schema in order, returning the first success.
Issue Code: 'union:no_match' when all branches fail
const id = v.union([
v.string()
.toTrimmed(),
v.number()
.integer()
.min(0),
])
id.execute('abc') // { value: 'abc' }
id.execute(123) // { value: 123 }
id.execute(true) // { issues: [{ code: 'union:no_match', ... }] }intersection(schemas, message?)
Runs all schemas and merges their results.
const auditable = v.object({ createdAt: v.number() })
const softDelete = v.object({ deletedAt: [v.number()] })
const entity = v.intersection([auditable, softDelete])
entity.execute({ createdAt: 1234567890 })
// { value: { createdAt: 1234567890, deletedAt: undefined } }instance(constructor, message?)
Validates that a value is an instance of the given constructor.
Issue Code: 'instance:expected_instance'
const dateSchema = v.instance(Date)
dateSchema.execute(new Date())
// { value: Date instance }
dateSchema.execute('2024-01-01')
// { issues: [{ code: 'instance:expected_instance', ... }] }Useful for:
Dateobjects- Custom class instances
- Built-in types like
Map,Set,RegExp
Nested Issue Paths
Structural validators automatically prepend keys or indexes to issue paths:
const schema = v.object({
items: v.array(
v.object({
id: v.string(),
}),
),
})
schema.execute({ items: [{ id: 123 }] })
// issues[0].path === ['items', 0, 'id']This makes it easy to highlight the exact failing field in forms or API responses.