/** * @fileoverview CON-04 Runtime Validation - Type Guard Factory * @description Composable type-guard helpers and factory functions * @version 0.18.4 */ import { z, ZodSchema } from "zod"; import { TypeGuardError } from "../utils/errors"; /** * Creates a type guard function from a Zod schema * Useful for runtime type checking in FE/Infra/ML code * * @param schema - Zod schema to create guard for * @param errorMessage - Optional custom error message * @returns Type guard function * * @example * ```typescript * const isUser = createTypeGuard(userSchema); * * function processUser(data: unknown) { * if (isUser(data)) { * // TypeScript knows data is User type here * console.log(data.email); * } else { * console.log("Invalid user data"); * } * } * ``` */ export function createTypeGuard( schema: ZodSchema, errorMessage?: string ): (obj: unknown) => obj is T { return (obj: unknown): obj is T => { try { schema.parse(obj); return true; } catch (err) { if (errorMessage) { console.warn(`${errorMessage}:`, err instanceof Error ? err.message : err); } return false; } }; } /** * Creates a strict type guard that throws TypeGuardError on validation failure * Useful when you need to ensure type safety at runtime * * @param schema - Zod schema to create guard for * @param errorMessage - Optional custom error message * @returns Type guard function that throws on validation failure * * @example * ```typescript * const assertIsUser = createStrictTypeGuard(userSchema, "Invalid user data"); * * function processUser(data: unknown) { * assertIsUser(data); // Throws if data is invalid * // TypeScript knows data is User type here * console.log(data.email); * } * ``` */ export function createStrictTypeGuard( schema: ZodSchema, errorMessage?: string ): (obj: unknown) => asserts obj is T { return (obj: unknown): asserts obj is T => { try { schema.parse(obj); } catch (err) { if (err instanceof z.ZodError) { throw new TypeGuardError( errorMessage || "Type guard validation failed", "TYPE_GUARD_ERROR", err, { zodError: err.format(), schemaName: schema._def?.description || 'Unknown schema' } ); } throw new TypeGuardError( errorMessage || "Type guard validation failed with unknown error", "TYPE_GUARD_ERROR", err instanceof Error ? err : undefined, { schemaName: schema._def?.description || 'Unknown schema', originalError: err } ); } }; } /** * Creates a safe type guard that returns a result object * Useful for graceful type checking without exceptions * * @param schema - Zod schema to create guard for * @returns Safe type guard function * * @example * ```typescript * const safeIsUser = createSafeTypeGuard(userSchema); * * function processUser(data: unknown) { * const result = safeIsUser(data); * if (result.success) { * // result.data is properly typed * console.log(result.data.email); * } else { * // result.error contains the ZodError * console.error("Invalid user:", result.error.format()); * } * } * ``` */ export function createSafeTypeGuard( schema: ZodSchema ): (obj: unknown) => { success: true; data: T } | { success: false; error: z.ZodError } { return (obj: unknown) => { const result = schema.safeParse(obj); if (result.success) { return { success: true, data: result.data }; } return { success: false, error: result.error }; }; } /** * Combines multiple type guards with AND logic * All guards must pass for the combined guard to pass * * @param guards - Array of type guard functions * @returns Combined type guard function * * @example * ```typescript * const isAdultUser = combineGuards([ * isUser, * (u): u is User & { age: number } => u.age >= 18 * ]); * ``` */ export function combineGuards( guards: Array<(obj: unknown) => obj is T> ): (obj: unknown) => obj is T { return (obj: unknown): obj is T => { return guards.every(guard => guard(obj)); }; } /** * Combines multiple type guards with OR logic * At least one guard must pass for the combined guard to pass * * @param guards - Array of type guard functions * @returns Combined type guard function * * @example * ```typescript * const isUserOrAdmin = combineGuardsOr([ * isUser, * isAdmin * ]); * ``` */ export function combineGuardsOr( guards: Array<(obj: unknown) => obj is T> ): (obj: unknown) => obj is T { return (obj: unknown): obj is T => { return guards.some(guard => guard(obj)); }; } /** * Creates a type guard for array elements * Validates that all elements in an array match the schema * * @param schema - Zod schema for array elements * @returns Type guard for arrays * * @example * ```typescript * const isUserArray = createArrayGuard(userSchema); * * function processUsers(data: unknown) { * if (isUserArray(data)) { * // TypeScript knows data is User[] * data.forEach(user => console.log(user.email)); * } * } * ``` */ export function createArrayGuard( schema: ZodSchema ): (obj: unknown) => obj is T[] { const elementGuard = createTypeGuard(schema); return (obj: unknown): obj is T[] => { return Array.isArray(obj) && obj.every(elementGuard); }; } /** * Creates a type guard for optional values * Validates that a value is either undefined or matches the schema * * @param schema - Zod schema for the value type * @returns Type guard for optional values * * @example * ```typescript * const isOptionalUser = createOptionalGuard(userSchema); * * function processUser(data: unknown) { * if (isOptionalUser(data)) { * // TypeScript knows data is User | undefined * if (data) { * console.log(data.email); * } * } * } * ``` */ export function createOptionalGuard( schema: ZodSchema ): (obj: unknown) => obj is T | undefined { return (obj: unknown): obj is T | undefined => { return obj === undefined || createTypeGuard(schema)(obj); }; }