Validation & Type Guards
assert()
Basic assertion with custom error messages and types.
ts
function assert(test: unknown, message?: string, ErrorClass?: typeof Error): voidExample:
ts
import { assert } from '@logosdx/utils'
// Basic assertions
const processLoanApplication = (app: unknown) => {
assert(app, 'Loan application is required')
assert(typeof app === 'object', 'Application must be an object')
assert(app.amount > 0, 'Loan amount must be positive')
assert(app.category, 'Category is required')
// Custom error types
assert(
['premium', 'standard', 'basic'].includes(app.category),
'Invalid category selection',
ValidationError
)
// Now TypeScript knows app is valid
return processValidApplication(app)
}
// Use in function parameters
const calculateInterest = (principal: number, rate: number, term: number) => {
assert(principal > 0, 'Principal must be positive')
assert(rate >= 0 && rate <= 1, 'Rate must be between 0 and 1')
assert(term > 0, 'Term must be positive')
assert(Number.isInteger(term), 'Term must be whole number of months')
return principal * rate * term
}assertObject()
Deep object validation with path-based assertions.
ts
function assertObject<T extends object>(
obj: T,
assertions: AssertionMap<T>
): void
type AssertionMap<T> = {
[P in PathNames<T>]?: (value: PathValue<T, P>) => [boolean, string]
}Example:
ts
import { assertObject, attempt } from '@logosdx/utils'
// Validate complex customer data
const validateCustomerData = (data: unknown) => {
assertObject(data, {
// Basic required fields
'id': (val) => [
typeof val === 'string' && val.length > 0,
'Customer ID must be non-empty string'
],
'profile.email': (val) => [
typeof val === 'string' && val.includes('@'),
'Email must be valid format'
],
'profile.name': (val) => [
typeof val === 'string' && val.trim().length >= 2,
'Name must be at least 2 characters'
],
// Financial validation
'loanHistory.totalLoans': (val) => [
typeof val === 'number' && val >= 0,
'Total loans must be non-negative number'
],
'loanHistory.averageAmount': (val) => [
typeof val === 'number' && val > 0,
'Average loan amount must be positive'
],
// Preference validation
'preferences.theme': (val) => [
['light', 'dark', 'auto'].includes(val),
'Theme must be light, dark, or auto'
],
// Optional field validation
'metadata.vipStatus': (val) => [
val === undefined || typeof val === 'boolean',
'VIP status must be boolean if present'
]
})
// If we get here, data is valid
return data as ValidatedCustomerData
}
// Use with API responses
const loadCustomerProfile = async (customerId: string) => {
const [rawData, fetchErr] = await attempt(() =>
fetch(`/api/customers/${customerId}`).then(r => r.json())
)
if (fetchErr) {
return { error: 'Could not load customer profile' }
}
const [customer, validationErr] = attemptSync(() =>
validateCustomerData(rawData)
)
if (validationErr) {
console.error('Customer data validation failed:', validationErr.message)
return {
error: 'Customer data is corrupted. Please contact support.',
details: validationErr.message
}
}
return { customer }
}Type Guards
Collection of utility functions for runtime type checking.
ts
// Basic type guards
function isFunction(a: unknown): a is Function
function isObject(a: unknown): a is Object
function isPlainObject(a: unknown): a is object
function isPrimitive(val: unknown): boolean
function isUndefined(val: unknown): val is undefined
function isDefined(val: unknown): val is NonNullable<unknown>
function isNull(val: unknown): val is null
// Collection validation
function allKeysValid<T extends object>(
item: T,
check: (value: T[keyof T], key: string | number) => boolean
): boolean
function allItemsValid<I extends Iterable<unknown>>(
item: I,
check: (value: unknown) => boolean
): booleanExample:
ts
import {
isFunction,
isObject,
isPrimitive,
isDefined,
allKeysValid,
allItemsValid
} from '@logosdx/utils'
// Build custom type guards
const isLoanApplication = (value: unknown): value is LoanApplication => {
if (!isObject(value)) return false
const obj = value as Record<string, unknown>
return (
typeof obj.id === 'string' &&
typeof obj.amount === 'number' &&
obj.amount > 0 &&
['personal', 'auto', 'home', 'business'].includes(obj.type) &&
isDefined(obj.applicant) &&
isObject(obj.applicant)
)
}
// Validate configuration objects
const validateConfig = (config: unknown) => {
if (!isObject(config)) return false
return allKeysValid(config, (value, key) => {
// All config values must be defined and not functions
return isDefined(value) && !isFunction(value)
})
}
// Validate arrays
const validateStringArray = (arr: unknown): arr is string[] => {
if (!Array.isArray(arr)) return false
return allItemsValid(arr, (item) => typeof item === 'string')
}
// Use in request handlers
const handleLoanApplication = (req: Request) => {
const body = req.body
if (!isLoanApplication(body)) {
return new Response('Invalid loan application format', { status: 400 })
}
// TypeScript now knows body is LoanApplication
return processLoanApplication(body)
}
// Environment detection
import { isBrowser, isNode, isReactNative } from '@logosdx/utils'
const setupEnvironment = () => {
if (isBrowser()) {
// Browser-specific setup
setupAnalytics()
registerServiceWorker()
}
else if (isNode()) {
// Node.js-specific setup
setupLogging()
connectToDatabase()
}
else if (isReactNative()) {
// React Native-specific setup
setupNativeModules()
}
}Error Types & Utilities
All flow control utilities throw specific error types for better error handling:
ts
// Error classes
class RetryError extends Error {}
class TimeoutError extends Error {}
class CircuitBreakerError extends Error {}
class RateLimitError extends Error {}
class ThrottleError extends Error {}
class AssertError extends Error {}
// Type guards
function isRetryError(error: unknown): error is RetryError
function isTimeoutError(error: unknown): error is TimeoutError
function isCircuitBreakerError(error: unknown): error is CircuitBreakerError
function isRateLimitError(error: unknown): error is RateLimitError
function isThrottleError(error: unknown): error is ThrottleError
function isAssertError(error: unknown): error is AssertErrorExample:
ts
import {
attempt,
isTimeoutError,
isRetryError,
isCircuitBreakerError,
isRateLimitError
} from '@logosdx/utils'
const handleLoanProcessing = async (application: LoanApplication) => {
const [result, err] = await attempt(() => processLoanWithAllProtections(application))
if (err) {
// Handle each error type specifically
if (isTimeoutError(err)) {
return {
error: 'Your application is taking longer than expected. Please wait while we process it.',
canRetry: true,
suggestedDelay: 30000
}
}
if (isRateLimitError(err)) {
return {
error: 'You\'re submitting applications too quickly. Please wait before trying again.',
canRetry: true,
suggestedDelay: err.retryAfter || 60000
}
}
if (isCircuitBreakerError(err)) {
return {
error: 'Our loan processing system is temporarily experiencing issues. Your application will be reviewed manually.',
canRetry: false,
fallbackAction: 'manual_review'
}
}
if (isRetryError(err)) {
return {
error: 'We encountered multiple issues processing your application. Please try again later.',
canRetry: true,
suggestedDelay: 300000 // 5 minutes
}
}
// Unknown error
console.error('Unexpected loan processing error:', err)
return {
error: 'An unexpected issue occurred. Please contact support if this persists.',
canRetry: false,
contactSupport: true
}
}
return { success: true, result }
}