Type-safe assertion library for TypeScript with runtime validation and type narrowing.
- 🎯 Type-safe - Full TypeScript support with type narrowing
- 🏗️ Custom exceptions - Use your own domain exceptions for Hexagonal/Clean Architecture
- 🚀 Zero dependencies - Lightweight and fast
- 🔒 Runtime validation - Validate unknown data at runtime
- 📦 Tree-shakeable - Only bundle what you use
- 🌐 Universal - Works in Node.js and browsers (see Browser Support)
npm install @frantisekstanko/assertion
import { Assertion } from '@frantisekstanko/assertion'
function processData(value: unknown) {
Assertion.string(value)
// value is now typed as string
}
Assert value is a string.
Assertion.string('hello') // ✓ passes
Assertion.string(123) // ✗ throws
Assert value is a finite number (not NaN or Infinity).
Assertion.number(42) // ✓ passes
Assertion.number(3.14) // ✓ passes
Assertion.number(NaN) // ✗ throws
Assertion.number(Infinity) // ✗ throws
Assert value is a number greater than a threshold.
Assertion.greaterThan(10, 5) // ✓ passes
Assertion.greaterThan(5, 5) // ✗ throws
Assertion.greaterThan(3, 5) // ✗ throws
Assert value is a number less than a threshold.
Assertion.lessThan(3, 5) // ✓ passes
Assertion.lessThan(5, 5) // ✗ throws
Assertion.lessThan(10, 5) // ✗ throws
Assert value is a string or null.
Assertion.nullOrString('hello') // ✓ passes
Assertion.nullOrString(null) // ✓ passes
Assertion.nullOrString(undefined) // ✗ throws
Assert value is a finite number or null.
Assertion.nullOrNumber(42) // ✓ passes
Assertion.nullOrNumber(null) // ✓ passes
Assertion.nullOrNumber(NaN) // ✗ throws
Assertion.nullOrNumber(Infinity) // ✗ throws
Assert value is not null or undefined.
Assertion.notNull('hello') // ✓ passes
Assertion.notNull(0) // ✓ passes
Assertion.notNull(null) // ✗ throws
Assertion.notNull(undefined) // ✗ throws
Assert value is an array.
Assertion.array([1, 2, 3]) // ✓ passes
Assertion.array([]) // ✓ passes
Assertion.array('not array') // ✗ throws
Assert value is a plain object (not an array or null).
Assertion.object({}) // ✓ passes
Assertion.object({ a: 1 }) // ✓ passes
Assertion.object([]) // ✗ throws
Assertion.object(null) // ✗ throws
Assert value is a boolean.
Assertion.boolean(true) // ✓ passes
Assertion.boolean(false) // ✓ passes
Assertion.boolean(1) // ✗ throws
Assert value is a boolean or null.
Assertion.nullOrBoolean(true) // ✓ passes
Assertion.nullOrBoolean(null) // ✓ passes
Assertion.nullOrBoolean(undefined) // ✗ throws
Assert value is a function.
Assertion.function(() => {}) // ✓ passes
Assertion.function(Math.max) // ✓ passes
Assertion.function({}) // ✗ throws
Assert value is a string or array with minimum length.
Assertion.minLength('hello', 3) // ✓ passes
Assertion.minLength([1, 2, 3], 2) // ✓ passes
Assertion.minLength('hi', 5) // ✗ throws
Assert value is a string or array with maximum length.
Assertion.maxLength('hello', 10) // ✓ passes
Assertion.maxLength([1, 2], 5) // ✓ passes
Assertion.maxLength('toolong', 3) // ✗ throws
Assert value is a string matching a regular expression.
Assertion.regex('abc123', /^[a-z]+[0-9]+$/) // ✓ passes
Assertion.regex('123abc', /^[a-z]+[0-9]+$/) // ✗ throws
Assert value is a valid email address.
Assertion.email('[email protected]') // ✓ passes
Assertion.email('invalid-email') // ✗ throws
Assert value is a valid URL.
Assertion.url('https://example.com') // ✓ passes
Assertion.url('not-a-url') // ✗ throws
Assert value is a valid UUID.
Assertion.uuid('550e8400-e29b-41d4-a716-446655440000') // ✓ passes
Assertion.uuid('invalid-uuid') // ✗ throws
Assert value is an instance of a constructor.
class MyClass {}
const instance = new MyClass()
Assertion.instanceOf(instance, MyClass) // ✓ passes
Assertion.instanceOf({}, MyClass) // ✗ throws
All assertion methods throw AssertionException
when validation fails.
You can catch and handle these exceptions:
import { Assertion, AssertionException } from '@frantisekstanko/assertion'
try {
Assertion.string(123)
} catch (error) {
if (error instanceof AssertionException) {
console.error('Validation failed:', error.message)
}
}
Keep your domain layer clean and vendor-agnostic by using your own exception classes. This is especially valuable in Hexagonal/Clean Architecture where you want to avoid coupling your domain to third-party libraries.
Extend the Assertion
class to throw your own domain exceptions:
import { Assertion } from '@frantisekstanko/assertion'
// Your domain exception in the core layer
class DomainValidationError extends Error {
constructor(message: string) {
super(message)
this.name = 'DomainValidationError'
}
}
// Create your domain-specific assertion class
class DomainAssertion extends Assertion {
protected static createException(message: string): Error {
return new DomainValidationError(message)
}
}
Now your domain layer only knows about DomainValidationError
, not the
assertion library:
// In your domain layer - no vendor coupling!
try {
DomainAssertion.string(userInput)
DomainAssertion.email(email)
} catch (error) {
if (error instanceof DomainValidationError) {
// Handle with your domain error handling strategy
}
}
This approach maintains the dependency inversion principle - your core domain doesn't depend on external libraries.
This library is fully compatible with modern browsers. It uses only standard JavaScript features available across all modern browser environments:
- No Node.js-specific APIs - Zero dependencies on Node.js runtime features
like
fs
,process
, orpath
- Universal JavaScript - Only uses standard APIs:
typeof
,Array.isArray()
,isNaN()
,isFinite()
, and theURL
constructor - Modern JavaScript - Compiled to ES2022, supported by all current browsers
You can use it directly in browser applications, web workers, or any JavaScript runtime without modification.
MIT © Frantisek Stanko