narrows
Version:
Super lean and simple object validation with TypeScript support.
67 lines • 3.56 kB
JavaScript
const isObject = (x) => typeof x === "object" && x !== null;
// -------------------------------- //
// - - - PRIMITIVE VALIDATORS - - - //
// -------------------------------- //
/** Returns true if and only if x is a boolean. */
export const boolean = (x) => typeof x === "boolean";
/** Returns true if and only if x is a string. */
export const string = (x) => typeof x === "string";
/** Returns true if and only if x is a number. */
export const number = (x) => typeof x === "number";
/** Returns true if and only if x is undefined. */
export const empty = (x) => x === undefined;
/** Returns true if and only if x is null. */
export const nil = (x) => x === null;
/** Returns true if and only if x is strictly equal to y. */
export const literal = (y) => (x) => x === y;
// ------------------------------ //
// - - - COMPLEX VALIDATORS - - - //
// ------------------------------ //
/** Returns true if and only if x is an object where each value matches the given validator. */
export const object = (validator) => (x) => isObject(x) && Object.values(x).every(validator);
/** Returns true if and only if x is an array where each element matches the given validator. */
export const array = (validator) => (x) => Array.isArray(x) && x.every(validator);
/** Returns true if and only if x is an instance of the given type. */
export const instance = (base) => (x) => x instanceof base;
// ------------------------------//
// - - - SCHEMA VALIDATORS - - - //
// ------------------------------//
/** Returns true if and only if x is an object where each value matches the validator at the corresponding key in the schema. */
export const record = (schema) => (x) => isObject(x) &&
Object.entries(schema).every(([key, validate]) => validate(x[key]));
/** Returns true if and only if x is an array where each element matches the validator at the corresponding index in the schema. */
export const tuple = (...schema) => (x) => Array.isArray(x) && schema.every((validator, i) => validator(x[i]));
// ----------------------- //
// - - - COMBINATORS - - - //
// ----------------------- //
/** Returns true if and only if x matches any of the given validators. */
export const any = (...validators) => (x) => validators.some(validator => validator(x));
/** Returns true if and only if x matches all of the given validators. */
export const all = (...validators) => (x) => validators.every(validator => validator(x));
/** Returns true if and only if x matches the given validator or is undefined. */
export const optional = (validator) => any(empty, validator);
/** Returns true if and only if x matches the given validator or is null. */
export const nullable = (validator) => any(nil, validator);
// --------------------- //
// - - - REPORTING - - - //
// --------------------- //
const canProxy = (x) => x !== null && (typeof x === "object" || typeof x === "function");
function spy(source, report, path = []) {
if (!canProxy(source))
return source;
return new Proxy(source, {
get(target, property) {
const value = target[property];
const next = [...path, property];
report(next);
return canProxy(value) ? spy(value, report, next) : value;
}
});
}
/** Takes a validator and an object to be validated. Returns null if the object is valid, or the path to the invalid property. */
export function report(validate, object) {
let path = [];
const passed = validate(spy(object, current => (path = current)));
return passed ? null : path;
}
//# sourceMappingURL=index.js.map