A list common TypeScript validator-functions and some useful utilities to go with them.
A simple, but long, list of validator-functions commonly used when checking values in TypeScript. This is not meant to replace advanced schema validation libraries like zod
, valibot
, jet-schema
etc. This is just a list of pre-defined validator-functions to save you time and boilerplate code in TypeScript.
import { isOptionalString, isRecord } from 'jet-validators';
if (isOptionalString(val)) {
// val is string | undefined
}
if (isRecord(val)) {
val['foo'] = 'bar';
}
- Contains validator-functions for the vast majority of real world scenarios you will encounter.
- For basic validators, there's no initialization step, you just import the validator-function and start using it.
- Overload regular expressions using environment variables.
- Contains some useful utilities for modifying values before validation.
- Also has some utilities for simple object schema validation.
- Zero dependency!
npm i -s jet-validators
These can be imported and used directly and don't require any configuration.
- isUndef
- isNull
- isNullOrUndef
- isBoolean
- isOptionalBoolean
- isNullableBoolean
- isNullishBoolean
- isBooleanArray
- isOptionalBooleanArray
- isNullableBooleanArray
- isNullishBooleanArray
Is it a valid boolean after calling the parseBoolean
utility function.
- isValidBoolean
- isOptionalValidBoolean
- isNullableValidBoolean
- isNullishValidBoolean
- isValidBooleanArray
- isOptionalValidBooleanArray
- isNullableValidBooleanArray
- isNullishValidBooleanArray
- isNumber
- isOptionalNumber
- isNullableNumber
- isNullishNumber
- isNumberArray
- isOptionalNumberArray
- isNullableNumberArray
- isNullishNumberArray
- isBigInt
- isOptionalBigInt
- isNullableBigInt
- isNullishBigInt
- isBigIntArray
- isOptionalBigIntArray
- isNullableBigIntArray
- isNullishBigIntArr
- isValidNumber
- isOptionalValidNumber
- isNullableValidNumber
- isNullishValidNumber
- isValidNumberArray
- isOptionalValidNumberArray
- isNullableValidNumberArray
- isNishValidNumArr
- isString
- isOptionalString
- isNullableString
- isNullishString
- isStringArray
- isOptionalStringArray
- isNullableStringArray
- isNullishStringArray
- isNonEmptyString
- isOptionalNonEmptyString
- isNullableNonEmptyString
- isNullishNonEmptyString
- isNonEmptyStringArray
- isOptionalNonEmptyStringArray
- isNullableNonEmptyStringArray
- isNullishNonEmptyStringArray
- TNonEmptyStr
- isSymbol
- isOptionalSymbol
- isNullableSymbol
- isNullishSymbol
- isSymbolArray
- isOptionalSymbolArray
- isNullableSymbolArray
- isNullishSymbolArray
Is argument an instance of Date
with a valid time value: "!isNaN(arg.getTime())" => true
- isDate
- isOptionalDate
- isNullableDate
- isNullishDate
- isDateArray
- isOptionalDateArray
- isNullableDateArray
- isNullishDateArray
Is argument a valid date after wrapping with new Date()
(could be Date
, string
, number
)
- isValidDate
- isOptionalValidDate
- isNullableValidDate
- isNullishValidDate
- isValidDateArray
- isOptionalValidDateArray
- isNullableValidDateArray
- isNullishValidDateArray
- isObject
- isOptionalObject
- isNullableObject
- isNullishObject
- isObjectArray
- isOptionalObjectArray
- isNullableObjectArray
- isNullishObjectArray
Checks if the argument is a non-null non-array object. Type predicate is Record<string, unknown>
.
- isRecord
- isOptionalRecord
- isNullableRecord
- isNullishRecord
- isRecordArray
- isOptionalRecordArray
- isNullableRecordArray
- isNullishRecordArray
- TRecord (type)
- isFunction
- isOptionalFunction
- isNullableFunction
- isNullishFunction
- isFunctionArray
- isOptionalFunctionArray
- isNullableFunctionArray
- isNullishFunctionArray
Verifies the argument matches the regular-expression. Note than an empty string will validate to true
for each function.
The regular expressions for each function below can be overwritten using the environment variables. To overload a regular expression create an environment variables with the format:
- JET_VALIDATORS_REGEX_{name of the function in uppercase} (i.e.
JET_VALIDATORS_REGEX_EMAIL=^\S+@\S+\.\S+$
)
- isColor
- isOptionalColor
- isNullableColor
- isNullishColor
- TColor (type)
- isEmail
- isOptionalEmail
- isNullableEmail
- isNullishEmail
- TEmail (type)
- isUrl
- isOptionalUrl
- isNullableUrl
- isNullishUrl
- TURL (type)
- isAlphaNumericString
- isOptionalAlphaNumericString
- isNullableAlphaNumericString
- isNullishAlphaNumericString
- TAlphabeticStr (type)
- isAlphabeticString
- isOptionalAlphabeticString
- isNullableAlphabeticString
- isNullishAlphabeticString
- TAlphaNumericStr (type)
These require an initialization step which will return a validator function.
- isInArray
- isOptionalInArray
- isNullableInArray
- isNullishInArray
Does the argument strictly equal any item in the array:
const isInArrTest = isInArray(['1', '2', '3']);
isInArrTest('1'); // => true
- isInRange
- isOptionalInRange
- isNullableInRange
- isNullishInRange
- isInRangeArray
- isOptionalInRangeArray
- isNullableInRangeArray
- isNullishInRangeArray
Will check if the argument (can be a number-string
or a number
) is in the provided range. The function will check if the argument is greater-than the first param and less-than the second param. If you wish to include the min or max value in the range (i.e. greater-than-or-equal-to) wrap it in square brackets. If you wish to leave off a min or max pass an empty array []
. If you want to check if the number is not between two numbers, use the bigger number for the first param and the lesser number for the second:
// Between 0 and 100
const isBetween0And100 = isInRange(0, 100);
isBetween0And100(50); // false
isBetween0And100('100'); // false
isBetween0And100(0); // true
// Is negative
const isNegative = isInRange([], 0);
isNegative(0); // false
isNegative(-.0001); // true
const isOptPositive = isOptionalInRange(0, []);
isOptPositive(undefined); // true
isOptPositive(1_000_000_000); // true
// 0 to 100
const isFrom0to100 = isInRange([0], [100]);
isFrom0to100('50'); // true
isFrom0to100(100); // true
isFrom0to100(0); // true
// less than 50 or greater than 100
const lessThan50OrGreaterThan100 = isInRange(100, 50);
lessThan50OrGreaterThan100(75); // false
lessThan50OrGreaterThan100(101); // true
- isKeyOf
- isOptionalKeyOf
- isNullableKeyOf
- isNullishKeyOf
- isKeyOfArray
- isOptionalKeyOfArray
- isNullableKeyOfArray
- isNullishKeyOfArray
Checks if the argument is a key of the object. Note that this will not work for symbols:
const someObject = {
foo: 'bar',
bada: 'bing',
} as const;
const isKeyofSomeObject = isKeyOf(someObject);
isKeyofSomeObject('foo'); // true
const isKeyofSomeObjectArr = isNullableKeyOfArray(someObject);
isKeyofSomeObjectArr(['bada', 'foo']); // true
- isEnum
- isOptionalEnum
- isNullableEnum
- isNullishEnum
- TEnum (type)
Check if the argument is a valid enum object. Unlike other complex validators, this does not require an inialization step. Note this will not work for mixed enum types: see: eslint@typescript-eslint/no-mixed-enums
:
enum StringEnum {
Foo = 'foo',
Bar = 'bar',
}
isEnum(StringEnum) // true
- isEnumVal
- isOptionalEnumVal
- isNullableEnumVal
- isNullishEnumVal
Check if the argument is a value of the enum. You must initialize this with a valid non-mixed enum type: see: eslint@typescript-eslint/no-mixed-enums
:
enum NumberEnum {
Foo,
Bar,
}
const isNumberEnumVal = isEnumVal(NumberEnum);
isNumberEnumVal(NumberEnum.Foo); // true
These complement the validator-functions and are useful if you need to modify a value before checking it or validate an object's schema. Utilities need to be imported using /utils
at the end of the library name:
import { parseObject } from 'jet-validators/utils';
Remove null
/undefined
from type-predicates and runtime validation:
const isString = nonNullable(isNullishString);
isString(null); // false
isString(undefined); // false
Loop through and object's key/value pairs and fire a callback for each one. If any callback returns false
, the whole function will return false
. It will also caste the return value to the generic if passed one. Note that this does not work recursively. This function is useful for dynamic objects where you don't know what the keys will be:
const isStrNumObj = iterateObjectEntries<Record<string, number>>((key, val) =>
isString(key) && isNumber(val));
isStrNumObj({ a: 1, b: 2, c: 3 }); // true
isStrNumObj({ a: 1, b: 2, c: 'asdf' }); // false
Accepts a transformation function for the first argument, a validator for the second, and returns a validator-function which calls the transform function before validating. The returned validator-function provides a callback as the second argument, if you need to access the transformed value. You should use transform
if you need to modify a value when using parseObject
or testObject
:
const isNumArrWithParse = transform((arg: string) => JSON.parse(arg), isNumberArray);
isNumArrWithParse('[1,2,3]', val => {
isNumberArray(val); // true
})); // true
- parseBoolean
- parseOptionalBoolean
- parseNullableBoolean
- parseNullishBoolean
Converts the following values to a boolean. Note will also covert the string equivalents:
"true" or true
:true
(case insensitive i.e."TrUe" => true
)"false" or false
:false
(case insensitive i.e."FaLsE" => false
)"1" or 1
:true
"0" or 0
:false
parseBoolean('tRuE'); // true
parseBoolean(1) // true
parseBoolean('0') // false
- parseJson
- parseOptionalJson
- parseNullableJson
- parseNullishJson
Calls the JSON.parse
function, if the argument is not a string an error will be thrown:
const numberArr = parseJson<number[]>('[1,2,3]');
isNumberArray(val); // true
If you need to validate an object schema, you can pass a validator object with the key being a property of the object and the value being any of the validator-functions in this library OR you can write your own validator-function (see the Custom Validators section).
These functions aren't meant to replace full-fledged schema validation libraries (like zod, ajv, etc), they're just meant as simple object validating tools where using a separate schema validation library might be overkill. If you need something more powerful, I highly recommend this repo's sister library jet-schema which allows you to do a lot more like force schema properties using predefined types.
- parseObject
- parseOptionalObject
- parseNullableObject
- parseNullishObject
- parseObjectArray
- parseOptionalObjectArray
- parseNullableObjectArray
- parseNullishObjectArray
This function iterates an object (and any nested object) and runs the validator-functions against each property. If every validator-function passed, the argument will be returned while purging any properties not in the schema. If it does not pass, then the function returns undefined
. You can optionally pass a error-handler function as the second argument which will fire whenever a validator-function fails. If the validator-function throws an error, it will be passed to the caughtErr
param (see below snippet):
interface IUser {
id: number;
name: string;
address: {
city: string;
zip: number;
}
}
const parseUser = parseObject({
id: transform(Number, isNumber),
name: isString,
address: {
city: isString,
zip: isNumber,
}
}, (property: string, value: unknown, caughtErr?: unknown) => {
throw Error(`Property "${property}" failed to pass validation.`)
});
const user: IUser = parseUser({
id: '5',
name: 'john',
email: '--',
address: {
city: 'seattle',
zip: 99999,
}
});
// 'user' variable above:
// {
// id: 5,
// name: 'john',
// address: {
// city: 'seattle',
// zip: 99999,
// }
// }
- If you use the
parseObjectArray
the error callback handler will also pass the index of the object calling the error function:
const parseUserArrWithError = parseObjectArray({
id: isNumber,
name: isString,
}, (prop, value, index) => {
// index "2" should call the error function.
});
parseUserArrWithError([
{ id: 1, name: '1' },
{ id: 2, name: '2' },
{ id: '3', name: '3' },
]);
- testObject
- testOptionalObject
- testNullableObject
- testNullishObject
- testObjectArray
- testOptionalObjectArray
- testNullableObjectArray
- testNullishObjectArray
Test object is nearly identical to parseObject
(it actually calls parseObject
under-the-hood) but returns a type-predicate instead of the argument passed. Transformed values and purging non-schema keys will still happen as well:
const user: IUser = {
id: '5',
name: 'john',
email: '--',
address: {
city: 'seattle',
zip: 99999,
}
}
const testUser = testObject(user);
if (testUser(user)) { // user is IUser
// 'user' variable above:
// {
// id: 5,
// name: 'john',
// address: {
// city: 'seattle',
// zip: '99999',
// }
// }
}
For parseObject
and testObject
you aren't restricted to the validator-functions in jet-validators
. You can write your own validator-function, just make sure your argument is unknown
and it returns a type predicate:
type TRelationalKey = number;
interface IUser {
id: TRelationalKey;
name: string;
}
// The custom validator-function
const isRelationalKey = (arg: unknown): arg is TRelationalKey => {
return isNumber(arg) && arg >= -1;
}
const parseUser = parseObject({
id: isRelationalKey,
name: isString,
});
const user: IUser = parseUser({
id: 5,
name: 'joe',
});
If you want to wrap the parseObject
or testObject
functions cause you want to let's say, apply the same error handler to multiple object-validators, you need to import the TSchema
type and have your generic extend it:
import { isNumber, isString } from 'jet-validators';
import { parseObject, TSchema } from 'jet-validators/utils';
const customParse = <U extends TSchema>(schema: U) => {
return parseObject(schema, (prop: string) => console.error(prop + ' failed validation'))
}
const parseUser = customParse({ id: isNumber, name: isString });
parseUser({ id: 5, name: 'joe' }); // => { id: 5, name: 'joe' }
- traverseObject
- traverseOptionalObject
- traverseNullableObject
- traverseNullishObject
- traverseObjectArray
- traverseOptionalObjectArray
- traverseNullableObjectArray
- traverseNullishObjectArray
Iterate over each key in an object (works recursively too) and fire a callback function for each key/value pair that is reached. This is useful if you need to modify an object before doing something with it. If any key/value pair is an array objects, each of those objects will be iterated over too.
Note that for
parseObject
andtestObject
you should wrap the validator-function withtransform
and not usetraverseObject
.traverseObject
is useful when you need to modify an object for some other validator likejasmine
orvitest
(that's what I use it for).
const convertValidToDateObjects = traverseObject((key, value, parentObj) => {
if (isValidDate(value)) {
parentObj[key] = new Date(value);
} else {
parentObj[key] = 'Invalid Date';
}
});
const result = convertValidToDateObjects({
today: '2024-12-06T23:43:37.012Z',
lastYear: '2023-12-06T22:14:20.012Z',
nested: {
milli: 1733528684737,
invalid: '2024-12-06TVB23:43:37.012Z',
dateArr: [1733528684737, 1733528684737, 1733528684737],
},
});
// 'result' variable above:
// {
// today: new Date('2024-12-06T23:43:37.012Z'),
// lastYear: new Date('2023-12-06T22:14:20.012Z'),
// nested: {
// milli: new Date(1733528684737),
// invalid: 'Invalid Date',
// dateArr: [
// new Date(1733528684737),
// new Date(1733528684737),
// new Date(1733528684737),
// ],
// },
// }