jet-validators ✈️
February 13, 2026 · View on GitHub
A comprehensive collection of TypeScript validator functions and utilities for common compile-time and runtime checks.
jet-validators' parseObject function is "JIT-optimized" and one of the fastest schema validation tools available without requiring a compilation step. Check out these benchmarks here.
📚 Table of Contents
Looking for every exported function? Check out the full export reference.
***
👋 Introduction
jet-validators is a large collection of small, composable validator functions commonly used when validating values in TypeScript.
- Drop-in validators you can import and use immediately
- Covers the vast majority of real-world validation needs
- Extremely fast and lightweight object schema validation
- Tiny: bundle + zip size only 4.7kb
- Zero dependencies
Quick Glance
import { isOptionalString } from 'jet-validators';
if (isOptionalString(value)) {
// value is string | undefined
}
***
📦 Installation
npm install jet-validators
***
🔰 Basic Validators
Basic validators can be imported directly and used without configuration.
All validators follow consistent naming patterns:
isXisOptionalXisNullableXisNullishXisXArray(and variants)
Nullables
isUndefisNullisNullish(null | undefined)
isBoolean
isBooleanisOptionalBooleanisNullableBooleanisNullishBooleanisBooleanArray(+ variants)
isValidBoolean
Valid after running through parseBoolean
isValidBooleanisOptionalValidBooleanisNullableValidBooleanisNullishValidBooleanisValidBooleanArray(+ variants)
isNumber
isNumber(+ optional / nullable / array variants)
Sub-categories:
- Positive
- Negative
- Unsigned
Each includes the full optional / nullable / array variants.
isInteger
Same structure as isNumber, including:
- Positive
- Negative
- Unsigned
isBigInt
isBigIntisOptionalBigIntisNullableBigIntisNullishBigIntisBigIntArray(+ variants)
isValidNumber
Valid after numeric coercion.
isValidNumberisOptionalValidNumberisNullableValidNumberisNullishValidNumberisValidNumberArray(+ variants)
isString
isString(+ optional / nullable / array variants)
isNonEmptyString
Ensures .length > 0.
isNonEmptyString(+ variants)
isSymbol
isSymbol(+ variants)
isDate
Checks for a Date instance with a valid timestamp.
!isNaN(date.getTime());
isDate(+ variants)
isValidDate
Accepts Date, string, or number and validates via new Date(...).
isValidDate(+ variants)
isObject
Checks for a non-null object.
isObject(+ variants)
isFunction
isFunction(+ variants)
***
🧠 Complex Validators
These require an initialization step and return a validator function.
isValidString
isValidString(+ variants)
- Options
This takes an options object and returns a string validator.
| option | type | description |
|---|---|---|
minLength | number | Minimum string length. Setting 0 allows '' (empty string) even if regex fails. |
maxLength | number | Maximum string length. |
length | number | Forces exact length (mutually exclusive with minLength/maxLength). |
regex | RegExp | Must pass the given regular expression. |
throws | boolean | Throw instead of returning false on validation failure. |
errorMessage | (value?: unknown, reason?: string) => string | Customize the thrown error message when throws is true. |
- Restrictions
| constraint | details |
|---|---|
| Exclusive lengths | Provide either the length field or the minLength/maxLength pair (never both). |
| Error customization | You can supply errorMessage only when throws is true. |
- Generics
You can supply a string generic if you want to narrow down the string type:
const typeValidator3 = isValidString<'foo'>({
regex: /^foo$/,
nullish: true,
});
// typeValidator3 => arg is 'foo' | null | undefined
Please see the test isValidString for a full example.
isInArray
const isAllowed = isInArray(['a', 'b', 'c']);
isAllowed('a'); // true
Supports optional / nullable variants.
isValidArray
Ensures every value in an array is contained in the validator array. Accepts optional minLength/maxLength arguments as well.
Note: This does not validate duplicates. It only checks that the argument is an array and every value present is allowed.
Please see the test isValidArray for a full example.
Supports optional / nullable variants.
isInRange
Checks whether a number (or numeric string) falls within a range.
Rules:
(min, max)→ exclusive[min], [max]→ inclusive[]→ no bound- Reverse bounds → “outside range”
const between0and100 = isInRange([0], [100]);
between0and100(50); // true
between0and100('100'); // true
const negative = isInRange([], 0);
negative(-1); // true
const outside = isInRange(100, 50);
outside(101); // true
outside(75); // false
⚙️ Utilities
Utilities are imported from:
import { parseObject } from 'jet-validators/utils';
nonNullable
Removes null and undefined from a validator.
const isStrictString = nonNullable(isNullishString);
makeOptional / makeNullable / makeNullish
Wrap custom validators to extend their accepted types.
type Email = `${string}@${string}`;
const isEmail = (arg: unknown): arg is Email =>
isString(arg) && EMAIL_REGEX.test(arg);
const isNullishEmail = makeNullish(isEmail); // --> "Email | null | undefined"
transform
Transforms a value before validation.
const isParsedArray = transform(JSON.parse, isNumberArray);
// isParsedArray is (arg: unknown, (transformedValue: number[]) => void): arg is number[]
Supports callbacks for accessing transformed values.
parseBoolean
Converts common boolean representations to actual booleans:
"true","false"(case-insensitive)"1","0"1,0
parseBoolean('TrUe'); // true
parseBoolean(0); // false
parseJson
Safely wraps JSON.parse.
const nums = parseJson<number[]>('[1,2,3]');
Throws if input is not a string.
***
📐 Object Schema Validation
Extremely fast and lightweight schema validation for objects using validator functions.
parseObject
- If valid, returns a full deep clone of the provided object; otherwise returns
false. - Removes unknown keys (by default)
- Can enforce strict type-safety on schemas with generics
- Can infer types from non-generic schemas.
- Optional error callback
const parseUser = parseObject<IUser>({
id: transform(Number, isNumber),
name: isString,
});
OR
const parseUser = parseObject({
id: transform(Number, isNumber),
name: isString,
});
parseUser; // <-- Inferred type is: `(arg: unknown) => arg is { id: number; name: string }`
No need to call
SomeUtilityType<typeof parseUser>
Supports:
- optional / nullable
- arrays
- nested schemas
- loose / strict modes
testObject
Same behavior as parseObject, but returns a type predicate.
if (testUser(user)) {
// user is IUser
}
Combining parse + test
Nested schemas may use testObject inside parseObject. Supplying generics restores full type safety. Note that you cannot use parseObject on a nested schema because it returns the tested object, not a type predicate:
interface IUser {
id: number;
name: string;
address: {
street: string;
city: string;
};
country?: {
name: string;
code: number;
};
}
const parseUser = parseObject<IUser>({
id: isUnsignedInteger,
name: isString,
// **testObject implied** address cannot be undefined
// and type safety will allow extra keys. Safety is
// inherited from root `parseUser` function
address: {
street: isString,
city: isString,
},
// **testObject explicit** undefined allowed with the
// optional option and strict type-safety is being
// enforced
country: testOptionalObject<IUser['country']>({
abbreviation: isString,
name: isString,
}),
});
parse/testObject features and examples
Error handling
You can pass a callback as the second argument to the parseObject function, or as the second argument to the function returned from it, to receive an array of errors when any occur. Each error object has the format:
| Field | Type | Description |
|---|---|---|
info | string | General information about the validation failure. |
functionName | string | Name of the validator function that failed. |
value | unknown | The value that caused the validation failure (optional). |
caught | string | Error message caught from an unsafe validator function, if any. |
key | string | The key at which the failure occurred but only when it happened at the root level. |
keyPath | string[] | Full path to the failing value for anything other than a key at the root level. If the failure occurs while inside an array variant (e.g. parseObjectArray), the first element represents the array index of the failing item. |
Example
const parseUsersArray = parseObjectArray({ name: isString });
parseUsersArray([{ name: 'sean' }, { name: 123 }], (errors) => ...);
// Errors callback param above will be:
[{
info: "Validator function returned false.",
functionName: "isString",
value: 123,
keyPath: ["1", "name"]
}]
Wrapping parse/test
You may want to wrap parseObject to ensure all parse functions throw the same custom Error object. When wrapping these utilities, ensure your generics extend Schema<T> to preserve type safety.
Please see the section Wrapping with Custom Validators around schemas for a full example.
Adding Custom Validators to schemas
Any function of the form (arg: unknown) => arg is T can be used in schemas. If your custom validator checks an object and has nested errors, and you want those errors to bubble up to the highest level, you need to provide a callback. Otherwise, parseObject will only see the custom validator itself as the failing function, and nested errors will be ignored.
Please see the section Adding Custom Validators to schemas for a full example.
Manually creating error arrays
If you want to set up your own error array, you need to use the setIsParseErrorArray function to mark it as such because, in the real world, there could be validator functions with callbacks for reasons other than error handling.
Please see the section Manually creating error arrays for a full example.
Getting the type for a parse/testObject function
If you need the type for a parse function you created, use the utility type ReturnType with typeof <yourParseFunction> as its generic argument.
Please see the unit-test Test setting a type for the parseObject for a full example.
Testing multiple layers of Schema
Due to TypeScript limitations, you'll lose type safety when trying to wrap parseObject and pass a Schema down to another Schema. Wrap the nested schema with testObject and a generic.
Please see the unit-test Testing multiple layers of Schema
for a full example.
Safety Modes
Control how extra object properties are handled. Nested schemas inherit the parent mode unless overridden:
- loose – keep extra keys
- default – remove extra keys (no error)
- strict – remove extra keys and emit errors
const strictUser = strictParseObject({
address: { street: isString }, // Will inherit strict from parent
country: looseTestObject(...), // override parent mode for this nested schema
});
***
📄 License
MIT © seanpmaxwell1