Package Exports
- guardz
- guardz/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (guardz) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
π‘οΈ Guardz
A powerful TypeScript type guard library with structured error handling for runtime type validation.
Runtime type guards with detailed error messages β not just validation, but actionable feedback.
Sample
Guardz is a comprehensive runtime type-checking library that goes beyond simple validation.
It provides detailed, structured error messages that help you identify exactly what went wrong and where.
- β Structured Error Messages - Know exactly what failed and why
- β Zero transformation
- β Fully type-safe
- β Human-readable guards
- β Tiny and dependency-free
- β Custom Error Handling - Integrate with your logging and monitoring
π Why Guardz?
TypeScript types vanish at runtime. Thatβs where Guardz steps in.
Unlike schema validators that require re-declaring types, Guardz uses your existing TS types as the source of truth, matching values without coercion.
But Guardz goes further:
- π Structured error messages: Instantly know what failed, where, and why. Every type guard can provide detailed, field-specific error messages for debugging, logging, and user feedback.
- π Custom error handling: Integrate with your logging, monitoring, or UI error display with a simple callback.
π Read: "Assert Nothing, Guard Everything"
π¦ Installation
npm install guardz
# or
yarn add guardz
Usage
Basic Type Guards
Start with simple primitive type checking:
import { isString } from 'guardz';
const data: unknown = getDataFromSomewhere();
if (isString(data)) { // data type is narrowed to string
console.log(data.toUpperCase());
}
Object Type Guards
Create type guards for complex object structures:
import { isType, isString } from 'guardz';
const data: unknown = getDataFromSomewhere();
// Build type guard function
const isUser = isType({
name: isString,
age: isString,
})
if (isUser(data)) { // data type is narrowed to { name: string, age: string }
console.log(`Name: ${data.name}`);
console.log(`Age: ${data.age}`);
}
Array Type Guards
Validate arrays with specific item types:
import { isArrayWithEachItem, isNumber } from 'guardz';
const data: unknown = getDataFromSomewhere()
if (isArrayWithEachItem(isNumber)(data)) { // data type is narrowed to number[]
console.log(data.map((item) => item.toFixed(2)))
}
Object Property Type Guards
Validate object properties with specific value types:
import { isObjectWithEachItem, isNumber } from 'guardz';
const data: unknown = getDataFromSomewhere()
if (isObjectWithEachItem(isNumber)(data)) { // data type is narrowed to Record<string, number | undefined>
console.log(data.something?.toFixed(2))
}
Union Type Guards
Handle multiple possible types:
import { isNumber, isString, isOneOfTypes } from 'guardz';
const data: unknown = getDataFromSomewhere()
if (isOneOfTypes<number | string>(isNumber, isString)(data)) { // data type is narrowed to string | number
return isNumber(data) ? data.toFixed(2) : data;
}
Composite Type Guards
Handle complex type relationships like intersections and extensions:
import { isIntersectionOf, isExtensionOf, isType, isString, isNumber } from 'guardz';
// For intersection types (Type A & Type B)
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: string;
department: string;
}
type PersonEmployee = Person & Employee;
const isPerson = isType<Person>({ name: isString, age: isNumber });
const isEmployee = isType<Employee>({ employeeId: isString, department: isString });
const isPersonEmployee = isIntersectionOf(isPerson, isEmployee);
const data: unknown = getDataFromSomewhere();
if (isPersonEmployee(data)) { // data type is narrowed to PersonEmployee
console.log(`${data.name} works in ${data.department}`);
}
// For inheritance patterns (Interface B extends Interface A)
interface Manager extends Person {
employeeId: string;
department: string;
managedTeamSize: number;
}
const isManagerFull = isType<Manager>({
name: isString,
age: isNumber,
employeeId: isString,
department: isString,
managedTeamSize: isNumber
});
const isManager = isExtensionOf(isPerson, isManagerFull);
if (isManager(data)) { // data type is narrowed to Manager
console.log(`Manager ${data.name} manages ${data.managedTeamSize} people`);
}
Nullable Type Guards
Handle null values:
import { isNullOr, isString } from 'guardz';
const data: unknown = getDataFromSomewhere()
if (isNullOr(isString)(data)) { // data type is narrowed to string | null
return data?.toUpperCase();
}
Optional Type Guards
Handle undefined values:
import { isUndefinedOr, isString } from 'guardz';
const data: unknown = getDataFromSomewhere()
if (isUndefinedOr(isString)(data)) { // data type is narrowed to string | undefined
return data?.toUpperCase();
}
Combined Nullable and Optional Types
Handle both null and undefined values:
import { isUndefinedOr, isNullOr, isNilOr, isString } from 'guardz';
// Method 1: Using isNilOr (recommended for brevity)
const data: unknown = getDataFromSomewhere()
if (isNilOr(isString)(data)) { // data type is narrowed to string | null | undefined
return data?.toUpperCase();
}
// Method 2: Explicit composition (equivalent to isNilOr)
if (isUndefinedOr(isNullOr(isString))(data)) { // data type is narrowed to string | undefined | null
return data?.toUpperCase();
}
Complex Nested Type Guards
Create type guards for deeply nested structures:
import { isUndefinedOr, isString, isEnum, isEqualTo, isNumber, isOneOfTypes, isArrayWithEachItem, isType } from 'guardz';
enum PriceTypeEnum {
FREE = 'free',
PAID = 'paid'
}
type Book = {
title: string;
price: PriceTypeEnum,
author: {
name: string;
email?: string;
};
chapters: Array<{
content: string;
startPage: number;
}>;
rating: Array<{
userId: string;
average: number | 'N/A';
}>
}
const data: unknown = getDataFromSomewhere()
const isBook = isType<Book>({
title: isString,
price: isEnum(PriceTypeEnum),
author: isType({
name: isString,
email: isUndefinedOr(isString),
}),
chapters: isArrayWithEachItem(isType({
content: isString,
startPage: isNumber,
})),
rating: isArrayWithEachItem(isType({
userId: isString,
average: isOneOfTypes<number | 'N/A'>(isNumber, isEqualTo('N/A'))
})),
})
if (isBook(data)) { // data type is narrowed to Book
return data;
}
Guard with Tolerance
Use guardWithTolerance
when you want to proceed with potentially invalid data while logging validation errors:
import { isBook } from 'isBook'; // see previous example
import { guardWithTolerance } from 'guardz';
const data: unknown = getDataFromSomewhere();
// This will return the data as Book type even if validation fails,
// but will log errors if config is provided
const book = guardWithTolerance(data, isBook, {
identifier: 'book',
callbackOnError: (error) => console.error('Validation error:', error)
});
Additional Type Guards
Integer Validation
Validate that a value is an integer number:
import { isInteger } from 'guardz';
const data: unknown = getDataFromSomewhere();
if (isInteger(data)) { // data type is narrowed to number
console.log(`User ID: ${data}`); // Safe to use as integer
}
Tuple Validation
Validate fixed-length arrays with specific types at each position:
import { isTuple, isString, isNumber } from 'guardz';
const data: unknown = getDataFromSomewhere();
// Check for [string, number] tuple
if (isTuple([isString, isNumber])(data)) { // data type is narrowed to [string, number]
const [name, age] = data;
console.log(`${name} is ${age} years old`);
}
BigInt Validation
Validate BigInt values for large numbers:
import { isBigInt } from 'guardz';
const data: unknown = getDataFromSomewhere();
if (isBigInt(data)) { // data type is narrowed to bigint
console.log(`Large number: ${data}`);
}
Additional Number Type Guards
Validate specific number ranges:
import { isNonPositiveNumber, isNegativeNumber } from 'guardz';
// Non-positive numbers (β€ 0) - includes zero
const data1: unknown = getDataFromSomewhere();
if (isNonPositiveNumber(data1)) { // data1 type is narrowed to NonPositiveNumber
console.log(`Value is zero or negative: ${data1}`);
}
// Negative numbers (< 0) - excludes zero
const data2: unknown = getDataFromSomewhere();
if (isNegativeNumber(data2)) { // data2 type is narrowed to NegativeNumber
console.log(`Value is strictly negative: ${data2}`);
}
Integer-Specific Type Guards
Validate integers with specific range constraints:
import {
isPositiveInteger,
isNegativeInteger,
isNonNegativeInteger,
isNonPositiveInteger
} from 'guardz';
// Positive integers (> 0 and whole numbers)
const userId: unknown = getUserInput();
if (isPositiveInteger(userId)) { // userId type is narrowed to PositiveInteger
console.log(`User ID: ${userId}`); // Safe for database primary keys
}
// Non-negative integers (β₯ 0 and whole numbers)
const arrayIndex: unknown = getArrayIndex();
if (isNonNegativeInteger(arrayIndex)) { // arrayIndex type is narrowed to NonNegativeInteger
console.log(`Array index: ${arrayIndex}`); // Safe for 0-based indexing
}
// Negative integers (< 0 and whole numbers)
const errorCode: unknown = getErrorCode();
if (isNegativeInteger(errorCode)) { // errorCode type is narrowed to NegativeInteger
console.log(`Error code: ${errorCode}`); // Safe for negative error codes
}
// Non-positive integers (β€ 0 and whole numbers)
const floorLevel: unknown = getFloorLevel();
if (isNonPositiveInteger(floorLevel)) { // floorLevel type is narrowed to NonPositiveInteger
console.log(`Floor level: ${floorLevel}`); // Safe for ground level and basements
}
π Structured Error Handling (Core Feature)
One of Guardz's most powerful features is its structured error messages. Every type guard can provide detailed, field-specific error messages that tell you:
- What failed (the value and its field)
- Where it failed (the property path)
- What was expected (the type or constraint)
Error Message Format
Expected {identifier} ({value}) to be "{expectedType}"
Examples:
Expected user.age ("30") to be "number"
Expected items ([]) to be "NonEmptyArray"
Expected config.port ("abc") to be "PositiveInteger"
How to Use
Every type guard accepts an optional config with an identifier
and a callbackOnError
:
const errors: string[] = [];
const config = {
identifier: 'user',
callbackOnError: (error: string) => errors.push(error),
};
const isUser = isType({ name: isString, age: isNumber });
const result = isUser({ name: 'John', age: '30' }, config);
// errors now contains: [ 'Expected user.age ("30") to be "number"' ]
Why It Matters
- Debugging: Instantly see which field failed and why
- User Feedback: Show clear, actionable error messages in your UI
- Logging/Monitoring: Integrate with your error tracking systems
- Testing: Assert on specific error messages in your tests
Advanced: Nested and Multiple Errors
Guardz tracks errors even in deeply nested structures, using dot/bracket notation for property paths:
Expected user.details.age ("thirty") to be "number"
Expected users[2].email (123) to be "string"
API Reference
Below is a comprehensive list of all type guards provided by guardz
.
Core Functions
isType
(propsTypesToCheck: { [P in keyof T]: TypeGuardFn<T[P]> }): TypeGuardFn Creates a type guard function for a specific object shapeT
. It checks if a value is a non-null object and verifies that each property specified inpropsTypesToCheck
conforms to its corresponding type guard function.guardWithTolerance
(data: unknown, typeGuardFn: TypeGuardFn Validates data using the provided type guard function. If validation fails, it still returns the data as the expected type but logs errors through the config callback., config?: Nullable ): T
Primitive Type Guards
- isAny - Always returns true for any value
- isBoolean - Checks if a value is a boolean
- isDate - Checks if a value is a Date object
- isDefined - Checks if a value is not null or undefined
- isNil - Checks if a value is null or undefined
- isNumber - Checks if a value is a valid number (excludes NaN)
- isString - Checks if a value is a string
- isUnknown - Always returns true for any value
Array Type Guards
- isArrayWithEachItem - Checks if a value is an array where each item matches a specific type
- isNonEmptyArray - Checks if a value is a non-empty array
- isNonEmptyArrayWithEachItem - Checks if a value is a non-empty array where each item matches a specific type
- isTuple - Checks if a value is a tuple (fixed-length array) with specific types at each position
Object Type Guards
- isNonNullObject - Checks if a value is a non-null object (excludes arrays)
- isObjectWithEachItem - Checks if a value is an object where each property value matches a specific type
- isPartialOf - Checks if a value is a partial object matching a specific type
String Type Guards
- isNonEmptyString - Checks if a value is a non-empty string
Number Type Guards
- isNonNegativeNumber - Checks if a value is a non-negative number (β₯ 0)
- isPositiveNumber - Checks if a value is a positive number (> 0)
- isNonPositiveNumber - Checks if a value is a non-positive number (β€ 0)
- isNegativeNumber - Checks if a value is a negative number (< 0)
- isInteger - Checks if a value is an integer number
- isPositiveInteger - Checks if a value is a positive integer (> 0 and whole number)
- isNegativeInteger - Checks if a value is a negative integer (< 0 and whole number)
- isNonNegativeInteger - Checks if a value is a non-negative integer (β₯ 0 and whole number)
- isNonPositiveInteger - Checks if a value is a non-positive integer (β€ 0 and whole number)
BigInt Type Guards
- isBigInt - Checks if a value is a BigInt
Union Type Guards
- isOneOf - Checks if a value matches one of several specific values
- isOneOfTypes - Checks if a value matches one of several type guards
Composite Type Guards
- isIntersectionOf - Validates a value against multiple type guards (intersection types:
A & B
) - isExtensionOf - Validates inheritance patterns where one type extends another (
interface B extends A
)
Nullable/Optional Type Guards
- isNullOr - Checks if a value is null or matches a specific type
- isUndefinedOr - Checks if a value is undefined or matches a specific type
- isNilOr - Checks if a value is null, undefined, or matches a specific type (equivalent to
isUndefinedOr(isNullOr(...))
)
Special Type Guards
- isEnum - Checks if a value matches any value from an enum
- isEqualTo - Checks if a value exactly equals a specific value
Utility Types
- NonEmptyArray
- Type for non-empty arrays - NonEmptyString - Type for non-empty strings
- NonNegativeNumber - Type for non-negative numbers (β₯ 0)
- NonPositiveNumber - Type for non-positive numbers (β€ 0)
- NegativeNumber - Type for negative numbers (< 0)
- Nullable
- Type for values that can be null - PositiveNumber - Type for positive numbers (> 0)
- Integer - Type for integer numbers
- PositiveInteger - Type for positive integers (> 0 and whole number)
- NegativeInteger - Type for negative integers (< 0 and whole number)
- NonNegativeInteger - Type for non-negative integers (β₯ 0 and whole number)
- NonPositiveInteger - Type for non-positive integers (β€ 0 and whole number)
Contributing
We welcome contributions! Whether you're fixing a bug, adding a new feature, or improving documentation, your help is appreciated.
How to Contribute
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Development Setup
# Clone the repository
git clone https://github.com/thiennp/guardz.git
cd guardz
# Install dependencies
npm install
# Run tests
npm test
# Build the project
npm run build
Code Style
- Follow the existing code style and formatting
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass before submitting
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
Getting Help
- π Documentation - This README contains comprehensive examples
- π Issues - Report bugs or request features on GitHub Issues
- π¬ Discussions - Ask questions and share ideas on GitHub Discussions
Show Your Support
If you find this library helpful, consider:
- β Starring the repository on GitHub
- πΊ Buying me a beer - PayPal
- π’ Sharing with your team and community