guardz
Version:
A simple and lightweight TypeScript type guard library for runtime type validation.
82 lines (81 loc) • 3.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isOneOfTypes = isOneOfTypes;
const stringify_1 = require("../stringify");
const getExpectedTypeName_1 = require("../utils/getExpectedTypeName");
/**
* Creates a type guard that checks if a value matches at least one of several type guards.
*
* This is different from `isOneOf` which checks against literal values. This function
* combines multiple type guard functions and returns true if the value passes any of them.
* Useful for union types where each type needs its own validation logic.
*
* @template T - The union type of all possible types
* @param typeGuards - Array of type guard functions to check against
* @returns A type guard function that validates against any of the provided type guards
*
* @example
* ```typescript
* import { isOneOfTypes, isString, isNumber, isBoolean, isType } from 'guardz';
*
* // Simple union types
* const isStringOrNumber = isOneOfTypes(isString, isNumber);
* const isPrimitive = isOneOfTypes(isString, isNumber, isBoolean);
*
* console.log(isStringOrNumber("hello")); // true
* console.log(isStringOrNumber(42)); // true
* console.log(isStringOrNumber(true)); // false
*
* // Complex union types
* interface User {
* type: "user";
* name: string;
* }
*
* interface Admin {
* type: "admin";
* permissions: string[];
* }
*
* const isUser = isType<User>({ type: isEqualTo("user"), name: isString });
* const isAdmin = isType<Admin>({ type: isEqualTo("admin"), permissions: isArrayWithEachItem(isString) });
* const isPerson = isOneOfTypes(isUser, isAdmin);
*
* const data: unknown = getUserInput();
* if (isPerson(data)) {
* // data is now typed as User | Admin
* console.log(data.type); // TypeScript knows this exists on both types
* }
* ```
*/
function isOneOfTypes(...typeGuards) {
return function (value, config) {
// Collect validation results to avoid duplicate checks
const validationResults = typeGuards.map(typeGuard => ({
typeGuard,
isValid: typeGuard(value, null)
}));
const isValid = validationResults.some(result => result.isValid);
if (!isValid && config) {
const valueString = (0, stringify_1.stringify)(value);
const displayValue = valueString.length > 200
? config.identifier
: `${config.identifier} (${valueString})`;
const errorMessages = [
`Expected ${displayValue} type to match one of "${typeGuards.map(fn => (0, getExpectedTypeName_1.getTypeGuardDisplayName)(fn)).join(' | ')}"`,
];
// Use the validation results we already collected
validationResults.forEach(({ typeGuard }) => typeGuard(value, {
...config,
callbackOnError: error => {
const newError = `- ${error}`;
if (!errorMessages.includes(newError)) {
errorMessages.push(newError);
}
},
}));
config.callbackOnError(errorMessages.join('\n'));
}
return isValid;
};
}