Deep Object Validation with Lodash conforms
This article explores how to implement strict, declarative object
validation in JavaScript using Lodash's _.conforms method.
You will learn the mechanics of _.conforms, how it maps
predicates to corresponding properties, and how nesting conform
predicates enables clean, highly maintainable deep structural checks
across complex nested objects without external validation libraries.
Understanding
_.conforms
Lodash provides _.conforms(source) to create a reusable
predicate function. The source argument is an object whose
properties map to validator functions (predicates). When the generated
function evaluates an input object, it verifies that every property in
source returns a truthy value when passed the corresponding
value from the target object.
const _ = require('lodash');
const isUser = _.conforms({
name: (val) => typeof val === 'string' && val.length > 0,
age: (val) => typeof val === 'number' && val >= 18
});
isUser({ name: 'Alice', age: 25 }); // true
isUser({ name: 'Bob', age: 16 }); // falseImplementing Deep Structural Checks
By default, _.conforms evaluates properties at the root
level of an object. To validate deeply nested structures, you can nest
_.conforms predicates within the schema definition. Because
_.conforms returns a standard predicate function
(value) => boolean, it seamlessly acts as the validator
for child properties.
const _ = require('lodash');
const validateUserProfile = _.conforms({
id: (id) => typeof id === 'string' && id.startsWith('usr_'),
account: _.conforms({
email: (email) => typeof email === 'string' && email.includes('@'),
status: (status) => ['active', 'suspended', 'pending'].includes(status),
settings: _.conforms({
notifications: (val) => typeof val === 'boolean',
theme: (val) => ['light', 'dark'].includes(val)
})
}),
roles: (roles) => Array.isArray(roles) && roles.length > 0 && roles.every(r => typeof r === 'string')
});Validating Nested Structures
When validateUserProfile is invoked, the evaluation
executes down the hierarchy:
const validPayload = {
id: 'usr_1024',
account: {
email: 'dev@example.com',
status: 'active',
settings: {
notifications: true,
theme: 'dark'
}
},
roles: ['admin', 'editor']
};
const invalidPayload = {
id: 'usr_2048',
account: {
email: 'dev@example.com',
status: 'active',
settings: {
notifications: 'yes', // Invalid: not a boolean
theme: 'dark'
}
},
roles: ['admin']
};
console.log(validateUserProfile(validPayload)); // true
console.log(validateUserProfile(invalidPayload)); // false_.conforms vs
_.conformsTo
Lodash also provides _.conformsTo(object, source). While
_.conformsTo evaluates an object immediately against a
source definition, _.conforms is a higher-order function
that produces a curried validator. For deeply mapped validation,
_.conforms is the preferred choice because each nested
level becomes a self-contained predicate that composes naturally within
parent conform objects or collection iterators like
_.filter.