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 });   // false

Implementing 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.