A comprehensive TypeScript type guard library with advanced validation capabilities, error reporting, and performance optimizations.
# npm
npm install guardz
# yarn
yarn add guardz
# pnpm
pnpm add guardz
Ensure your tsconfig.json
includes:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"exactOptionalPropertyTypes": true
}
}
import { isNonEmptyString, isNumber, isString, isType } from 'guardz';
// Basic guard
const value: unknown = getUnknownValue();
if(isString(value)) {
// TypeScript now knows value is a string - type narrowing in action
console.log(value.length); // Safe to access string properties
}
// Simulate getting data from an external source (API, database, etc.)
const maybeUser: unknown = await getUserInfoFromServer();
// Define a type guard for user type checking
const isUser = isType<UserDTO>({
id: isNumber,
name: isNonEmptyString,
email: isNonEmptyString,
});
// Use type guard for type narrowing
if (isUser(maybeUser)) {
// TypeScript now knows maybeUser is a valid UserDTO
console.log(maybeUser.id);
console.log(maybeUser.name);
console.log(maybeUser.email);
}
Guardz is a type guard library, not a validation library.
Guardz focuses on providing lightweight, composable type guards that enable TypeScript's type narrowing while maintaining excellent performance. For a detailed explanation of the differences between type guards and validators, see this comprehensive guide.
- Comprehensive Type Guards: 50+ built-in type guards for all JavaScript types
- Advanced Type Narrowing: Array type guards, object type guards, union types, and more
- TypeScript Integration: Seamless type narrowing with exact type inference
- Performance Optimized: Fast type guards with minimal overhead
- TypeScript First: Full type safety with precise type inference
- Zero Dependencies: Lightweight with no external dependencies
- Tree Shaking: Optimized for bundle size with tree shaking support
import { isString, isNumber, isBoolean, isSymbol, isArrayWithEachItem, isObject } from 'guardz';
isString('hello'); // true
isNumber(42); // true
isBoolean(true); // true
isSymbol(Symbol('a')); // true
isArrayWithEachItem(isNumber)([1, 2, 3]); // true
isObject({ a: 1 }); // true
import { isSymbol } from 'guardz';
const value: unknown = Symbol('test');
if (isSymbol(value)) {
// value is now typed as symbol
console.log(value.toString());
}
import { isType, isString, isNumber, isPositiveInteger, isUndefinedOr } from 'guardz';
interface User {
id: number;
name: string;
email: string;
age?: number;
}
const isUser = isType<User>({
id: isPositiveInteger,
name: isString,
email: isString,
age: isUndefinedOr(isNumber), // Handle optional property
});
const user = { id: 1, name: 'John', email: 'john@example.com' };
console.log(isUser(user)); // true
import { isArrayWithEachItem, isString, isNumber } from 'guardz';
const isStringArray = isArrayWithEachItem(isString);
const isNumberArray = isArrayWithEachItem(isNumber);
console.log(isStringArray(['a', 'b', 'c'])); // true
console.log(isNumberArray([1, 2, 3])); // true
import { isString, isNumber, isType, by } from 'guardz';
const isUser = isType({ name: isString, age: isNumber });
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: '30' }, // invalid
{ name: 'Charlie', age: 35 },
];
// β This won't work due to parameter mismatch
// const validUsers = users.filter(isUser);
// β
Use by for simple filtering
const validUsers = users.filter(by(isUser));
console.log('Valid users:', validUsers);
import { isOneOfTypes, isString, isNumber, isBoolean } from 'guardz';
const isPrimitive = isOneOfTypes(isString, isNumber, isBoolean);
console.log(isPrimitive('hello')); // true
console.log(isPrimitive(42)); // true
console.log(isPrimitive(true)); // true
console.log(isPrimitive({})); // false
import { isType, isString, isNumber } from 'guardz';
const errors: string[] = [];
const config = {
identifier: 'user',
callbackOnError: (error: string) => errors.push(error),
errorMode: 'multi', // 'single' | 'multi' | 'json'
};
const isUser = isType({
name: isString,
age: isNumber,
});
const invalidUser = { name: 123, age: 'thirty' };
const result = isUser(invalidUser, config);
console.log(result); // false
console.log(errors); // ['user.name: Expected string, got number (123)', 'user.age: Expected number, got string ("thirty")']
import { isRegex, isPattern } from 'guardz';
import type { Pattern } from 'guardz';
// Validate RegExp objects
const patterns: unknown[] = [/^[a-z]+$/, new RegExp('\\d+'), 'not a regex'];
patterns.forEach(pattern => {
if (isRegex(pattern)) {
console.log(`Valid RegExp: ${pattern.source} (flags: ${pattern.flags})`);
}
});
// Create branded types for pattern-matched strings
type Email = Pattern<'Email'>;
type PhoneNumber = Pattern<'PhoneNumber'>;
type URL = Pattern<'URL'>;
const isEmail = isPattern<'Email'>(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
const isPhoneNumber = isPattern<'PhoneNumber'>('^\\+?[\\d\\s\\-()]{10,}$');
const isUrl = isPattern<'URL'>('^https?:\\/\\/.+');
// Validate strings against patterns
const email: unknown = 'user@example.com';
if (isEmail(email)) {
// email is now typed as Email (branded string)
console.log(`Valid email: ${email}`);
}
const phone: unknown = '+1-555-123-4567';
if (isPhoneNumber(phone)) {
// phone is now typed as PhoneNumber (branded string)
console.log(`Valid phone: ${phone}`);
}
// Type safety with branded types
function processEmail(email: Email) {
// This function only accepts validated email strings
console.log(`Processing email: ${email}`);
}
function processPhoneNumber(phone: PhoneNumber) {
// This function only accepts validated phone number strings
console.log(`Processing phone: ${phone}`);
}
// These work with type safety:
const validEmail: unknown = 'user@example.com';
if (isEmail(validEmail)) {
processEmail(validEmail); // TypeScript knows this is safe
}
// These would cause TypeScript errors:
// processEmail('invalid-email'); // Error: Argument of type 'string' is not assignable to parameter of type 'Email'
// processPhoneNumber('123'); // Error: Argument of type 'string' is not assignable to parameter of type 'PhoneNumber'
import { isType, isString, isNumber, isArrayWithEachItem, isOneOf } from 'guardz';
interface ApiResponse<T> {
data: T;
status: 'success' | 'error';
message: string;
timestamp: number;
}
const isUserResponse = isType<ApiResponse<User>>({
data: isType({
id: isNumber,
name: isString,
email: isString,
}),
status: isOneOf('success', 'error'), // Use isOneOf for exact values
message: isString,
timestamp: isNumber,
});
// Validate API responses
const response = await fetch('/api/users/1');
const data = await response.json();
if (isUserResponse(data)) {
console.log('Valid user:', data.data.name);
} else {
console.log('Invalid response format');
}
import { isType, isString, isNumber, isPositiveInteger } from 'guardz';
interface RegistrationForm {
username: string;
email: string;
age: number;
password: string;
}
const isRegistrationForm = isType<RegistrationForm>({
username: isString,
email: isString, // Could add email regex validation
age: isPositiveInteger,
password: isString,
});
const formData = {
username: 'john_doe',
email: 'john@example.com',
age: 25,
password: 'secure123',
};
if (isRegistrationForm(formData)) {
// Process valid form data
await registerUser(formData);
} else {
// Handle validation errors
showValidationErrors(errors);
}
import { isType, isString, isNumber, isDate } from 'guardz';
interface DatabaseUser {
id: number;
username: string;
email: string;
created_at: string;
updated_at: string;
}
const isDatabaseUser = isType<DatabaseUser>({
id: isNumber,
username: isString,
email: isString,
created_at: isString,
updated_at: isString,
});
// Validate database results
const users = await db.query('SELECT * FROM users');
const validUsers = users.filter(isDatabaseUser);
import { isType, isString, isNumber, isBoolean } from 'guardz';
interface AppConfig {
port: number;
database: {
host: string;
port: number;
name: string;
};
features: {
auth: boolean;
caching: boolean;
};
}
const isAppConfig = isType<AppConfig>({
port: isNumber,
database: isType({
host: isString,
port: isNumber,
name: isString,
}),
features: isType({
auth: isBoolean,
caching: isBoolean,
}),
});
// Validate environment configuration
const config = {
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
database: {
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 5432,
name: process.env.DB_NAME || 'myapp',
},
features: {
auth: process.env.AUTH_ENABLED === 'true',
caching: process.env.CACHE_ENABLED === 'true',
},
};
if (!isAppConfig(config)) {
throw new Error('Invalid configuration');
}
Create reusable type guard functions for consistent validation patterns:
import { isString, isNumber, isPositiveInteger, isType } from 'guardz';
// Create semantic type guards using existing functions
const isUserId = isPositiveInteger; // Already a reusable type guard
const isEmail = isString; // Already a reusable type guard
const isName = isString; // Already a reusable type guard
// Use them consistently across your application
interface User {
id: number; // Uses isUserId (isPositiveInteger)
name: string; // Uses isName (isString)
email: string; // Uses isEmail (isString)
}
const isUser = isType<User>({
id: isUserId,
name: isName,
email: isEmail,
});
For complex generic types with conditional properties, use factory functions with isType
:
import { isType, isString, isNumber, isUndefinedOr } from 'guardz';
// Define generic types with conditional properties
type ApiKeysSelect<T extends boolean = true> = {
name: T extends true ? string : string | undefined;
collectionPermissions: T extends true ? string : string | undefined;
updatedAt: T extends true ? string : string | undefined;
createdAt: T extends true ? string : string | undefined;
enableAPIKey: T extends true ? boolean : boolean | undefined;
apiKey: T extends true ? string : string | undefined;
apiKeyIndex: T extends true ? number : number | undefined;
};
// Create type guard factory for generic types
export const isApiKeysSelect = <T extends boolean = true>(
typeGuardT: TypeGuardFn<T>,
): TypeGuardFn<ApiKeysSelect<T>> =>
isType<ApiKeysSelect<T>>({
name: isUndefinedOr(typeGuardT),
collectionPermissions: isUndefinedOr(typeGuardT),
updatedAt: isUndefinedOr(typeGuardT),
createdAt: isUndefinedOr(typeGuardT),
enableAPIKey: isUndefinedOr(typeGuardT),
apiKey: isUndefinedOr(typeGuardT),
apiKeyIndex: isUndefinedOr(typeGuardT),
});
// Usage
const isRequiredApiKeys = isApiKeysSelect(isString);
const isOptionalApiKeys = isApiKeysSelect(isUndefinedOr(isString));
π‘ Pro Tip: For complex generic type validation with multiple conditional properties, consider using guardz-generator which automatically generates type guards for generic types and handles conditional properties efficiently.
// Multiple generic parameters
type Container<T, U, V extends boolean = true> = {
primary: T;
secondary: U;
metadata: V extends true ? { timestamp: number; version: string } : undefined;
};
// Factory for multiple generic parameters
export const isContainer = <T, U, V extends boolean = true>(
primaryGuard: TypeGuardFn<T>,
secondaryGuard: TypeGuardFn<U>,
metadataGuard?: TypeGuardFn<{ timestamp: number; version: string }>,
): TypeGuardFn<Container<T, U, V>> =>
isType<Container<T, U, V>>({
primary: primaryGuard,
secondary: secondaryGuard,
metadata: metadataGuard ? isUndefinedOr(metadataGuard) : undefined,
});
// Usage
const isStringNumberContainer = isContainer(isString, isNumber);
const isStringNumberContainerWithMetadata = isContainer(
isString,
isNumber,
isType({ timestamp: isNumber, version: isString })
);
import { isType, isString, isNumber, isArrayWithEachItem } from 'guardz';
// Optimized for repeated validation
const isUser = isType({
id: isNumber,
name: isString,
tags: isArrayWithEachItem(isString),
});
// Reuse the same guard instance
const users = [/* large array of user objects */];
const validUsers = users.filter(isUser); // Fast validation
// Use appropriate error modes for performance
const fastConfig = { errorMode: 'single' }; // Fastest
const detailedConfig = { errorMode: 'multi' }; // More detailed
const treeConfig = { errorMode: 'json' }; // Most detailed
// Zod
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
// Guardz
import { isType, isNumber, isString } from 'guardz';
const isUser = isType({
id: isNumber,
name: isString,
email: isString, // Add custom email validation if needed
});
// Joi
import Joi from 'joi';
const userSchema = Joi.object({
id: Joi.number().required(),
name: Joi.string().required(),
email: Joi.string().email().required(),
});
// Guardz
import { isType, isNumber, isString } from 'guardz';
const isUser = isType({
id: isNumber,
name: isString,
email: isString,
});
// Yup
import * as yup from 'yup';
const userSchema = yup.object({
id: yup.number().required(),
name: yup.string().required(),
email: yup.string().email().required(),
});
// Guardz
import { isType, isNumber, isString } from 'guardz';
const isUser = isType({
id: isNumber,
name: isString,
email: isString,
});
Problem: Type 'unknown' is not assignable to parameter of type 'TypeGuardFn<T>'
Solution: Ensure you're using the correct type guard function:
// β Wrong
const isUser = isType({
id: isNumber, // This is correct
name: 'string', // β This is wrong - should be isString
});
// β
Correct
const isUser = isType({
id: isNumber,
name: isString, // β
Use the type guard function
});
Problem: Validation is slow with large objects
Solution: Use appropriate error modes and optimize validation:
// β Slow - collects all errors
const result = isUser(data, { errorMode: 'multi' });
// β
Fast - stops at first error
const result = isUser(data, { errorMode: 'single' });
// β
Fastest - no error collection
const result = isUser(data);
Problem: Large bundle size
Solution: Use tree shaking and import only what you need:
// β Imports everything
import * as guardz from 'guardz';
// β
Only imports what you need
import { isType, isString, isNumber } from 'guardz';
Enable detailed error reporting:
const errors: string[] = [];
const config = {
identifier: 'user',
callbackOnError: (error: string) => {
errors.push(error);
console.log('Validation error:', error);
},
errorMode: 'multi',
};
const result = isUser(data, config);
console.log('All errors:', errors);
single
(default): Fastest, stops at first errormulti
: Medium speed, collects all errors with detailsjson
: Slowest, provides hierarchical error tree structure
// For production - fast validation
const isUserFast = isType({
id: isNumber,
name: isString,
email: isString,
});
// For development - detailed errors
const isUserDev = isType({
id: isNumber,
name: isString,
email: isString,
});
// Usage
const config = process.env.NODE_ENV === 'production'
? undefined
: { errorMode: 'multi', callbackOnError: console.error };
// Create type guards once, reuse them
const isUser = isType({
id: isNumber,
name: isString,
email: isString,
});
// Reuse the same guard instance
const validateUsers = (users: unknown[]) => {
return users.filter(isUser);
};
isString
- Validates that a value is a stringisNumber
- Validates that a value is a valid number (excludes NaN)isBoolean
- Validates that a value is a booleanisBigInt
- Validates that a value is a BigIntisFunction
- Validates that a value is a function (including regular functions, arrow functions, class constructors, and methods)isSymbol
- Validates that a value is a Symbol
isAsserted
- Always returns true and asserts value is T (useful for 3rd party types without runtime validation)isEnum
- Creates a type guard that checks if a value matches any value from an enumisEqualTo
- Creates a type guard that checks if a value is exactly equal to a specific value using strict equality (===)isRegex
- Validates that a value is a RegExp objectisPattern<P>
- Creates a type guard that validates strings against a regex pattern and returns a branded type
isArrayWithEachItem<T>(typeGuard: TypeGuardFn<T>)
- Creates a type guard that validates arrays where each item matches a specific typeisNonEmptyArray
- Validates that a value is a non-empty arrayisNonEmptyArrayWithEachItem<T>(typeGuard: TypeGuardFn<T>)
- Creates a type guard that validates non-empty arrays where each item matches a specific type
isNonNullObject
- Validates that a value is a non-null object (excludes arrays)isType<T>(schema: Record<string, TypeGuardFn<any>>)
- Creates a type guard that validates objects against a schemaisObject<T>(schema: Record<string, TypeGuardFn<any>>)
- Alias for isType - creates a type guard for a specific object shapeisObjectWith<T>(schema: Record<string, TypeGuardFn<any>>)
- Alias for isType - validates objects with specific propertiesisObjectWithEachItem<T>(typeGuard: TypeGuardFn<T>)
- Creates a type guard that validates plain objects where each property value matches a specific type (only checks enumerable own properties)
isInteger
- Validates that a value is an integer (whole number)isPositiveInteger
- Validates that a value is a positive integer (greater than 0 and a whole number)isNegativeInteger
- Validates that a value is a negative integer (less than 0 and a whole number)isNonNegativeInteger
- Validates that a value is a non-negative integer (0 or greater and a whole number)isNonPositiveInteger
- Validates that a value is a non-positive integer (0 or less and a whole number)isPositiveNumber
- Validates that a value is a positive number (greater than 0)isNegativeNumber
- Validates that a value is a negative number (less than 0)isNonNegativeNumber
- Validates that a value is a non-negative number (0 or greater)isNonPositiveNumber
- Validates that a value is a non-positive number (0 or less)isNumeric
- Validates that a value is numeric (a number or a string that can be converted to a number)isDateLike
- Validates that a value can be treated as a Date (Date object, date string, or numeric timestamp)
isNonEmptyString
- Validates that a value is a non-empty string (trims whitespace, so strings containing only whitespace are considered empty)
isNil
- Validates that a value is null or undefinedisNullOr
- Creates a type guard that validates if a value is either null or matches a specific typeisUndefinedOr
- Creates a type guard that validates if a value is either undefined or matches a specific typeisNilOr
- Creates a type guard that validates if a value is either of type T, null, or undefined
isOneOf
- Creates a type guard that checks if a value matches one of several specific values using strict equality (===)isOneOfTypes
- Creates a type guard that checks if a value matches at least one of several type guards
isDate
- Validates that a value is a valid Date object (excludes invalid dates likenew Date("invalid")
)isError
- Validates that a value is an Error objectisBlob
- Validates that a value is a Blob objectisFile
- Validates that a value is a File objectisFileList
- Validates that a value is a FileList objectisFormData
- Validates that a value is a FormData objectisURL
- Validates that a value is a URL objectisURLSearchParams
- Validates that a value is a URLSearchParams object
isMap
- Validates that a value is a Map objectisSet
- Validates that a value is a Set objectisIndexSignature
- Creates a type guard that validates objects with index signatures (objects with dynamic keys of a specific type and values of a specific type)
isUnknown
- Always returns true (useful for unknown types)isAny
- Always returns true (useful for any types)isDefined
- Validates that a value is defined (not null/undefined)isBooleanLike
- Validates that a value can be treated as a boolean (boolean, "true"/"false", "1"/"0", or 1/0)isExtensionOf
- Creates a type guard for types that extend a base type by combining a base type guard with validation for additional properties (useful for inheritance patterns)isIntersectionOf
- Creates a type guard that validates a value against multiple type guards, ensuring the value satisfies all of themisPartialOf
- Creates a type guard that validates partial objects matching a specific type (allows missing properties)isTuple
- Creates a type guard that validates tuples (fixed-length arrays with specific types at each position)
isSchema
- Creates a type guard function for object schemas with improved nested type support (automatically handles nested structures)isShape
- Alias for isSchemaisNestedType
- Alias for isSchema
isBranded
- Creates a type guard function for a branded type using a predicate function (validates and narrows to branded types)guardWithTolerance
- Validates data against a type guard but returns the data regardless of validation result (useful for logging errors while proceeding with potentially invalid data)
generateTypeGuardError
- Generates error messages for type guard failures
PredicateFn
- A predicate function that validates a value and returns true if valid, false otherwise
toNumber
- Converts a Numeric value to a number (safely converts validated numeric values)toDate
- Converts a DateLike value to a Date object (safely converts validated date values)toBoolean
- Converts a BooleanLike value to a boolean (safely converts validated boolean values)
ValidationError
- Error type for validation failures with path, expected type, actual value, and messageValidationTree
- Tree structure for hierarchical validation errorsValidationResult
- Result type for validation operations with validity status and errorsValidationContext
- Context type for validation operations with path and configuration
Branded<T, B>
- Branded type with a specific brand identifier (supports string and unique symbol brands)BrandSymbols
- Predefined brand symbols for common use casesBrandedWith<T, K>
- Utility type to create branded types with predefined symbolsNonEmptyArray<T>
- Array type that cannot be emptyNonEmptyString
- String type that cannot be emptyPositiveNumber
- Number type that must be positiveNegativeNumber
- Number type that must be negativeNonNegativeNumber
- Number type that must be non-negativeNonPositiveNumber
- Number type that must be non-positivePositiveInteger
- Integer type that must be positiveNegativeInteger
- Integer type that must be negativeNonNegativeInteger
- Integer type that must be non-negativeNonPositiveInteger
- Integer type that must be non-positiveInteger
- Integer typeNullable<T>
- Type that can be nullNumeric
- Numeric type (number or string that can be converted to number)DateLike
- Date-like type (Date object, date string, or numeric timestamp)BooleanLike
- Boolean-like type (boolean, "true"/"false", "1"/"0", or 1/0)Pattern<P>
- Branded string type that matches a specific regex pattern
Guardz provides detailed error reporting with configurable error handling:
import { isType, isString, isNumber } from 'guardz';
const errors: string[] = [];
const config = {
identifier: 'user',
callbackOnError: (error: string) => errors.push(error),
errorMode: 'multi', // 'single' | 'multi' | 'json'
};
const isUser = isType({
name: isString,
age: isNumber,
});
const invalidUser = { name: 123, age: 'thirty' };
const result = isUser(invalidUser, config);
console.log(errors);
// [
// 'user.name: Expected string, got number (123)',
// 'user.age: Expected number, got string ("thirty")'
// ]
single
- Basic error messages (default)multi
- Detailed error messages with valuesjson
- Hierarchical error tree structure
Guardz provides utility types for enhanced type safety:
import type { TypeGuardFn, TypeGuardFnConfig } from 'guardz';
// TypeGuardFn<T> - Type guard function type
// TypeGuardFnConfig - Configuration for type guards
Guardz supports branded types with unique symbols for enhanced type safety:
import { isBranded, Branded, BrandSymbols, type BrandedWith } from 'guardz';
// Custom unique symbol brands
const UserIdBrand = Symbol('UserId');
type UserId = Branded<number, typeof UserIdBrand>;
const isUserId = isBranded<UserId>((value) => {
return typeof value === 'number' && value > 0 && Number.isInteger(value);
});
// Predefined brand symbols
type Email = BrandedWith<string, 'Email'>;
const isEmail = isBranded<Email>((value) => {
if (typeof value !== 'string') return false;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
});
// Usage
const id: unknown = 123;
if (isUserId(id)) {
// id is now typed as UserId (number & { readonly brand: typeof UserIdBrand })
console.log(id);
}
We welcome contributions! Please see our Contributing Guide for details.
git clone https://github.com/thiennp/guardz.git
cd guardz
npm install
npm test
This project is licensed under the MIT License - see the LICENSE file for details.
- API Reference: GitHub README
- Showcases: Complete TypeScript Examples - All possible TypeScript cases and real-world scenarios
- Examples: Practical Usage Examples - Ready-to-run code samples
- CHANGELOG: Version History - Latest updates and changes
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: nguyenphongthien@gmail.com
- guardz-generator - Code generator for complex type guards and generic types
- guardz-cli - Command-line interface for Guardz
- guardz-vscode - VS Code extension for Guardz
Thanks to all contributors and the TypeScript community for making this project possible.
Made with β€οΈ by the Guardz Team