Function argument validation for humans with expressive, chainable API
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advanced validation for objects and arrays including shape validation, property checking, element validation, and structural constraints.
Comprehensive object validation with shape matching, property validation, and instance checking.
/** Object validation predicate */
interface ObjectPredicate extends BasePredicate<object> {
// Structure validation
readonly plain: ObjectPredicate;
readonly empty: ObjectPredicate;
readonly nonEmpty: ObjectPredicate;
// Property validation
hasKeys(...keys: string[]): ObjectPredicate;
hasAnyKeys(...keys: string[]): ObjectPredicate;
valuesOfType<T>(predicate: BasePredicate<T>): ObjectPredicate;
deepValuesOfType<T>(predicate: Predicate<T>): ObjectPredicate;
// Shape validation
partialShape<S>(shape: S): ObjectPredicate;
exactShape<S>(shape: S): ObjectPredicate;
deepEqual(expected: object): ObjectPredicate;
instanceOf(instance: Function): ObjectPredicate;
}
/** Shape definition for object validation */
type Shape = {
[key: string]: BasePredicate<any> | Shape;
};Structure Validation Examples:
import ow from 'ow';
// Structure validation
ow({}, ow.object.plain); // Plain object, not class instance
ow({}, ow.object.empty); // Empty object
ow({ name: 'Alice' }, ow.object.nonEmpty); // Non-empty object
// Class instance vs plain object
class User { constructor(public name: string) {} }
ow(new User('Alice'), ow.object.instanceOf(User));
ow({ name: 'Alice' }, ow.object.plain);Property Validation Examples:
import ow from 'ow';
const user = { name: 'Alice', age: 30, address: { city: 'NYC' } };
// Property existence (supports dot-notation)
ow(user, ow.object.hasKeys('name', 'age'));
ow(user, ow.object.hasKeys('address.city'));
ow(user, ow.object.hasAnyKeys('name', 'email'));
// Value type validation
ow({ a: 1, b: 2, c: 3 }, ow.object.valuesOfType(ow.number));
ow(
{ nums: [1, 2], strs: ['a', 'b'] },
ow.object.deepValuesOfType(ow.any(ow.array, ow.string, ow.number))
);Shape Validation Examples:
import ow from 'ow';
// Partial shape - ignores extra properties
const userShape = {
name: ow.string,
age: ow.number.integer.positive
};
ow({ name: 'Alice', age: 30, extra: 'ignored' }, ow.object.partialShape(userShape));
// Exact shape - fails on extra properties
ow({ name: 'Alice', age: 30 }, ow.object.exactShape(userShape));
// Nested shape validation
const nestedShape = {
user: {
name: ow.string,
contacts: {
email: ow.optional.string,
phone: ow.optional.string
}
}
};
ow({
user: {
name: 'Alice',
contacts: {
email: 'alice@example.com'
}
}
}, ow.object.exactShape(nestedShape));
// Deep equality
ow({ a: 1, b: [2, 3] }, ow.object.deepEqual({ a: 1, b: [2, 3] }));Comprehensive array validation with length constraints, element validation, and content checking.
/** Array validation predicate */
interface ArrayPredicate extends BasePredicate<unknown[]> {
// Length validation
length(length: number): ArrayPredicate;
minLength(length: number): ArrayPredicate;
maxLength(length: number): ArrayPredicate;
// Content validation
startsWith<T>(searchElement: T): ArrayPredicate;
endsWith<T>(searchElement: T): ArrayPredicate;
includes<T>(...searchElements: T[]): ArrayPredicate;
includesAny<T>(...searchElements: T[]): ArrayPredicate;
deepEqual<T>(expected: T[]): ArrayPredicate;
// State validation
readonly empty: ArrayPredicate;
readonly nonEmpty: ArrayPredicate;
// Type validation
ofType<U>(predicate: BasePredicate<U>): ArrayPredicate;
exactShape(predicates: BasePredicate[]): ArrayPredicate;
}Length Validation Examples:
import ow from 'ow';
// Length constraints
ow([1, 2, 3], ow.array.length(3));
ow([1, 2], ow.array.minLength(2));
ow([1, 2, 3], ow.array.maxLength(5));
ow([], ow.array.empty);
ow([1], ow.array.nonEmpty);Content Validation Examples:
import ow from 'ow';
const numbers = [1, 2, 3, 4, 5];
// Element position validation
ow(numbers, ow.array.startsWith(1));
ow(numbers, ow.array.endsWith(5));
// Content inclusion
ow(numbers, ow.array.includes(2, 3, 4));
ow(numbers, ow.array.includesAny(2, 10, 20));
// Deep equality
ow([[1, 2], [3, 4]], ow.array.deepEqual([[1, 2], [3, 4]]));Type Validation Examples:
import ow from 'ow';
// All elements must match predicate
ow([1, 2, 3], ow.array.ofType(ow.number.positive));
ow(['a', 'b', 'c'], ow.array.ofType(ow.string.nonEmpty));
// Mixed type arrays
ow([1, 'hello', true], ow.array.ofType(ow.any(ow.number, ow.string, ow.boolean)));
// Exact shape validation - elements match predicates at same indices
ow(['Alice', 30, true], ow.array.exactShape([
ow.string,
ow.number.integer.positive,
ow.boolean
]));
// Array of objects
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
];
ow(users, ow.array.ofType(ow.object.exactShape({
name: ow.string,
age: ow.number.integer.positive
})));import ow from 'ow';
// Complex nested structure
const apiResponse = {
users: [
{
id: 1,
profile: {
name: 'Alice',
contacts: ['alice@example.com', '+1-555-0123']
},
permissions: ['read', 'write']
}
],
meta: {
total: 1,
page: 1
}
};
const responseShape = {
users: ow.array.ofType(ow.object.exactShape({
id: ow.number.integer.positive,
profile: ow.object.exactShape({
name: ow.string.nonEmpty,
contacts: ow.array.ofType(ow.string.nonEmpty)
}),
permissions: ow.array.ofType(ow.string.oneOf(['read', 'write', 'admin']))
})),
meta: ow.object.exactShape({
total: ow.number.integer.positive,
page: ow.number.integer.positive
})
};
ow(apiResponse, ow.object.exactShape(responseShape));import ow from 'ow';
// Validate object with dynamic keys
const dynamicObject = {
'user:1': { name: 'Alice', active: true },
'user:2': { name: 'Bob', active: false }
};
// All keys must match pattern, all values must match shape
ow(Object.keys(dynamicObject), ow.array.ofType(ow.string.matches(/^user:\d+$/)));
ow(dynamicObject, ow.object.valuesOfType(ow.object.exactShape({
name: ow.string,
active: ow.boolean
})));import ow from 'ow';
// Validate transformed arrays
const input = ['1', '2', '3'];
const numbers = input.map(Number);
ow(input, ow.array.ofType(ow.string.numeric));
ow(numbers, ow.array.ofType(ow.number.integer));Install with Tessl CLI
npx tessl i tessl/npm-ow