guardz
Version:
A simple and lightweight TypeScript type guard library for runtime type validation.
136 lines (135 loc) • 4.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNestedType = exports.isShape = void 0;
exports.isSchema = isSchema;
const isNonNullObject_1 = require("./isNonNullObject");
const validationUtils_1 = require("../utils/validationUtils");
const typeGuardMeta_1 = require("../utils/typeGuardMeta");
/**
* Creates a type guard function for object schemas with improved nested type support.
*
* This function is similar to `isType` but provides better support for deeply nested structures
* by automatically handling nested type guards without requiring explicit `isType` calls for each level.
*
* Key differences from `isType`:
* - Automatically detects and handles nested object structures
* - Provides better error messages for nested validation failures
* - Supports both inline object definitions and existing type guards
* - Maintains backward compatibility with existing type guard patterns
*
* @template T - The type to validate
* @param schema - Object mapping property keys to their type guard functions or inline object definitions
* @returns A type guard function that validates the object structure
*
* @example
* ```typescript
* import { isSchema, isString, isNumber, isBoolean } from 'guardz';
*
* // Simple nested structure
* const isUser = isSchema({
* name: isString,
* age: isNumber,
* address: {
* street: isString,
* city: isString,
* zipCode: isNumber,
* },
* preferences: {
* theme: isString,
* notifications: isBoolean,
* },
* });
*
* // Complex nested structure with arrays
* const isComplexUser = isSchema({
* id: isNumber,
* profile: {
* name: isString,
* email: isString,
* contacts: [{
* type: isString,
* value: isString,
* }],
* },
* settings: {
* theme: isString,
* notifications: {
* email: isBoolean,
* push: isBoolean,
* },
* },
* });
* ```
*/
function isSchema(schema) {
// Validate input
if (!(0, isNonNullObject_1.isNonNullObject)(schema, null)) {
throw new TypeError('schema must be a non-null object');
}
const processedSchema = processNestedSchema(schema);
function isSchemaGuard(value, config) {
const errorMode = config?.errorMode || 'multi';
if (errorMode === 'multi' || errorMode === 'json') {
const context = {
path: config?.identifier || 'root',
config: config || null
};
const result = (0, validationUtils_1.validateObject)(value, processedSchema, context);
(0, validationUtils_1.reportValidationResults)(result, config || null);
return result.valid;
}
if (!(0, isNonNullObject_1.isNonNullObject)(value, config))
return false;
return Object.keys(processedSchema).every(function (key) {
const typeGuardFn = processedSchema[key];
if (!typeGuardFn)
return false;
return typeGuardFn(value[key], config ? { ...config, identifier: `${config.identifier}.${key}` } : null);
});
}
return (0, typeGuardMeta_1.attachTypeGuardMeta)(isSchemaGuard, { schema: processedSchema });
}
function resolveSchemaEntry(value) {
if (typeof value === 'function') {
return value;
}
if (Array.isArray(value)) {
return createArrayTypeGuard(value);
}
if (typeof value === 'object' && value !== null) {
return isSchema(value);
}
return value;
}
/**
* Process a nested schema by converting inline object definitions to type guards
*/
function processNestedSchema(schema) {
const processed = {};
for (const [key, value] of Object.entries(schema)) {
processed[key] = resolveSchemaEntry(value);
}
return processed;
}
/**
* Create a type guard for arrays of objects
*/
function createArrayTypeGuard(arraySchema) {
const itemSchema = arraySchema[0];
const itemTypeGuard = isSchema(itemSchema);
function isArrayGuard(value, config) {
if (!Array.isArray(value)) {
if (config) {
config.callbackOnError(`Expected ${config.identifier} to be an array`);
}
return false;
}
return value.every((item, index) => {
return itemTypeGuard(item, config ? { ...config, identifier: `${config.identifier}[${index}]` } : null);
});
}
return (0, typeGuardMeta_1.attachTypeGuardMeta)(isArrayGuard, { itemGuard: itemTypeGuard });
}
// Aliases for backward compatibility and flexibility
exports.isShape = isSchema;
exports.isNestedType = isSchema;