guardz
Version:
A simple and lightweight TypeScript type guard library for runtime type validation.
105 lines (104 loc) • 3.16 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isBranded = isBranded;
const generateTypeGuardError_1 = require("./generateTypeGuardError");
/**
* Creates a type guard function for a branded type.
*
* This utility allows developers to create custom type guards for branded types
* by providing a predicate function that returns true if the value is valid.
*
* @template T - The base type
* @template U - The branded type (extends Branded<T, any>)
* @param predicate - A PredicateFn that validates the value and returns true if valid, false otherwise
* @returns A type guard function that validates and narrows to the branded type
*
* @example
* ```typescript
* import { isBranded } from 'guardz';
*
* // String-based branded type
* type UserId = Branded<number, 'UserId'>;
*
* // Create a type guard for UserId
* const isUserId = isBranded<UserId>((value) => {
* return typeof value === 'number' && value > 0 && Number.isInteger(value);
* });
*
* // Usage
* const id: unknown = 123;
* if (isUserId(id)) {
* // id is now typed as UserId
* console.log(id); // number & { readonly brand: 'UserId' }
* }
*
* // With error handling
* const invalidId: unknown = -1;
* isUserId(invalidId, {
* callbackOnError: (error) => console.error(error),
* identifier: 'userId'
* }); // false, calls error callback
* ```
*
* @example
* ```typescript
* // Email validation with string brand
* type Email = Branded<string, 'Email'>;
*
* const isEmail = isBranded<Email>((value) => {
* if (typeof value !== 'string') return false;
* const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
* return emailRegex.test(value);
* });
*
* // Usage
* const email: unknown = 'user@example.com';
* if (isEmail(email)) {
* // email is now typed as Email
* console.log(email); // string & { readonly brand: 'Email' }
* }
* ```
*
* @example
* ```typescript
* // Unique symbol branded types
* const UserIdBrand = Symbol('UserId') as unique symbol;
* type UserIdSymbol = Branded<number, typeof UserIdBrand>;
*
* const isUserIdSymbol = isBranded<UserIdSymbol>((value) => {
* return typeof value === 'number' && value > 0 && Number.isInteger(value);
* });
*
* // Usage
* const id: unknown = 456;
* if (isUserIdSymbol(id)) {
* // id is now typed as UserIdSymbol
* console.log(id); // number & { readonly brand: typeof UserIdBrand }
* }
* ```
*
* @example
* ```typescript
* // Complex validation with multiple checks
* type Age = Branded<number, 'Age'>;
*
* const isAge = isBranded<Age>((value) => {
* if (typeof value !== 'number') return false;
* if (!Number.isInteger(value)) return false;
* if (value < 0) return false;
* if (value > 150) return false;
* return true;
* });
* ```
*/
function isBranded(predicate) {
return function (value, config) {
if (!predicate(value)) {
if (config) {
config.callbackOnError((0, generateTypeGuardError_1.generateTypeGuardError)(value, config.identifier, 'branded type validation'));
}
return false;
}
return true;
};
}