type-guard performs a runtime check that guarantees the type and value of properties in some scopes.
🚀 Node.js ready for launch.
🚀 Angular ready for launch.
In a Domain Driven Design approach, it is very useful to check constraints on Domain Entity and ValueType properties.
It relies on Tyr
to:
- Call one of the seven specific Guards like
string()
ornumber()
. - Call chained rule checkers.
- Return a
GuardResult
instance containing the reason for the failure of the guard, or a success.
It also relies on GuardResultBulk
to manage multiple Tyr
invocations:
- Combine and return the first
GuardResult
instance in failure, or only aGuardResult
instance in success. - Stack and return all
GuardResult
instances in failure, or only aGuardResult
instance in success.
- Installation
- Basic Usage
- Tyr
- GuardResultBulk
- Codex
- TypeDoc
- Changelog
- The true story
- Maintainer
- License
npm install @xtitusx/type-guard
In order to check and return a GuardResult
instance, just invoke Tyr
, call a specific Guard, and finish by calling guard(propertyValue: unknown, options?: IGuardResultOptions)
method which contains the property value and options.
- A simple type property check:
const guardResult = Tyr.string().guard("Lorem ipsum", { propertyName = "lipsum" });
console.log(guardResult);
// => GuardResult { success: true, propertyName: 'lipsum' }
- Multiple chained rule checkers:
const guardResult = Tyr.string().isAlpha().contains('ipsum').hasMaxLength(100).isTrimmed('left').guard("Lorem ipsum");
console.log(guardResult);
// => GuardResult {
// success: false,
// propertyName: undefined,
// message: 'string is expected to only contain basic Latin characters but does not: Lorem ipsum'
// }
const guardResult = Tyr.number().isMultiple(2).isMultiple(3).isMultiple(4).guard(12);
console.log(guardResult);
// => GuardResult { success: true, propertyName: undefined }
- A fast validation returning simply a boolean:
const success = Tyr.number().isBetween(10, 20).isEven().guard(14).isSuccess();
console.log(success);
// => true
- A thrown exception with a
GuardResult
instance message:
const guardResult = Tyr.dateString().isIso8601Date().guard('29-02-2021');
if (!guardResult.isSuccess()) {
throw new Error(guardResult.getMessage());
}
// => throw new Error(guardResult.getMessage());
// ^
//
// Error: DateStringGuard expected a date string but received: string
// ...
Tyr.array()
, Tyr.dateString()
, Tyr.number()
, and Tyr.string()
methods expect an IGuardOptions
object optionaly:
export interface IGuardOptions {
/**
* A single type of rule, the last called in the chain, is retained by the guard.
* @defaultValue false
*/
overrideRule?: boolean;
}
- overrideRule = false (default)
const stringGuard = Tyr.string({ overrideRule: false }).isTrimmed('right').hasMinLength(2).hasMaxLength(100);
const guardResult1 = stringGuard.contains('dummy').guard('Lorem Ipsum is simply dummy text');
console.log(guardResult1);
// => GuardResult { success: true, propertyName: undefined }
const guardResult2 = stringGuard
.contains('random')
.guard('Contrary to popular belief, Lorem Ipsum is not simply random text');
console.log(guardResult2);
// => GuardResult {
// success: false,
// propertyName: undefined,
// message: 'string is expected to contain dummy but does not: Contrary to popular belief, Lorem Ipsum is not simply random text'
// }
- overrideRule = true
const stringGuard = Tyr.string({ overrideRule: true }).isTrimmed('right').hasMinLength(2).hasMaxLength(100);
const guardResult1 = stringGuard.contains('dummy').guard('Lorem Ipsum is simply dummy text');
console.log(guardResult1);
// => GuardResult { success: true, propertyName: undefined }
const guardResult2 = stringGuard
.contains('random')
.guard('Contrary to popular belief, Lorem Ipsum is not simply random text');
console.log(guardResult2);
// => GuardResult { success: true, propertyName: undefined }
guard()
method expects an IGuardResultOptions
object optionaly:
interface IGuardResultOptions {
propertyName?: string;
/**
* The custom message used instead of the default message.
*/
customMessage?: string;
}
const guardResult = Tyr.string().contains('bar').guard('foo', { propertyName: 'prop'});
console.log(guardResult);
// => GuardResult {
// success: false,
// propertyName: 'prop',
// message: 'string is expected to contain bar but does not: foo'
// }
The message provided by getMessage()
method is also automatically prefixed with the name of the property if available:
console.log(guardResult.getMessage());
// => Property prop has failed the guard validation: string is expected to contain bar but does not: foo
const guardResult = Tyr.string().contains('bar').guard('foo', { customMessage: 'The input field must contain bar' });
console.log(guardResult);
// => GuardResult {
// success: false,
// propertyName: undefined,
// message: 'The input field must contain bar'
//}
Rule checker | Description |
---|---|
isEmpty() | Checks if array is empty. |
isNotEmpty() | Checks if array is not empty. |
hasSize(value: number) | Checks if array's length is equal to the specified number. |
hasMinSize(min: number) | Checks if array's length is equal or greater than to the specified number. |
hasMaxSize(max: number) | Checks if array's length is equal or smaller than the specified number. |
contains(value: unknown) | Checks if array contains the specified value. |
Rule checker | Description |
---|---|
isTrue() | Checks if value is a true boolean. |
isFalse() | Checks if value is a false boolean. |
Rule checker | Description |
---|---|
isInstanceOf(value: Function) | Checks if the prototype property of the param constructor appears anywhere in the prototype chain of the guarded object. |
Rule checker | Description |
---|---|
isIso8601Date() | Checks if date string is a valid and existing ISO 8601 Date (YYYY-MM-DD). |
isRfc3339() | Checks if date string is a valid and existing RFC 3339 Datetime. |
isSame(value: string) | Checks if date string is the same that the specified date. |
isSameOrBefore(value: string) | Checks if date string is the same or before the specified date. |
isSameOrAfter(value: string) | Checks if date string is the same or after the specified date. |
isBefore(value: string) | Checks if date string is strictly before the specified date. |
isAfter(value: string) | Checks if date string is strictly after the specified date. |
Rule checker | Description |
---|---|
isUndefined() | Checks if value is undefined. |
isNotUndefined() | Checks if value is not undefined. |
isNull | Checks if value is null. |
isNotNull() | Checks if value is not null. |
isNil() | Checks if value is undefined or null. |
isNotNil() | Checks if value is neither undefined nor null. |
Rule checker | Description |
---|---|
equals(value: number) | Checks if two numbers are equals. |
isMin(min: number) | Checks if number is equal or greater than to the specified number. |
isMax(max: number) | Checks if number is equal or smaller than to the specified number. |
isBetween(min: number, max: number) | Checks if number is within a closed interval. |
isIn(values: number[]) | Checks if number is in an array of allowed number values. |
isNotIn(values: number[]) | Checks if number is not in an array of disallowed number values. |
isPositive() | Checks if number is greater than zero. |
isNegative() | Checks if number is smaller than zero. |
isWhole() | Checks if number is a whole number. |
hasMaxFractionDigits(max: number) | Checks if the fractional part of number is equal or smaller than the specified number. |
isEven() | Checks if number is even. |
isOdd() | Checks if number is odd. |
isMultiple(value: number) | Checks if number is a multiple of the specified number. |
isPrime() | Checks if number is a prime number. |
isComposite() | Checks if number is a composite number. |
isFibonacci(allowNegative?: boolean) | Checks if number is a Fibonacci or a NegaFibonacci number. |
isNetworkPort(range?: NetworkPortRange) | Checks if number is a Network Port. |
Rule checker | Description |
---|---|
equals(value: string) | Checks if two string are equals. |
notEquals(value: string) | Checks if two string are not equals. |
contains(value: string, pos?: StringPosition) | Checks if string contains the specified substring. |
notContains(value: string) | Checks if string does not contain the specified substring. |
matches(value: RegExp) | Checks if string matches the specified regex. |
isIn(values: string[]) | Checks if string is in an array of allowed string values. |
isNotIn(values: string[]) | Checks if string is not in an array of disallowed string values. |
isEmpty() | Checks if string is empty. |
isNotEmpty() | Checks if string is not empty. |
hasLength(value: number) | Checks if string's length is equal to the specified number. |
hasMinLength(min: number) | Checks if string's length is equal or greater than to the specified number. |
hasMaxLength(max: number) | Checks if string's length is equal or smaller than the specified number. |
isUpperCase() | Checks if string does not contain any lowercase alpha characters. |
isLowerCase() | Checks if string does not contain any uppercase alpha characters. |
isCapitalized(style: CapitalizationStyle, checkFirstCharIsLetter?: boolean) | Checks if string follows a capitalization style. |
isProgrammingCase(convention: ProgrammingConvention) | Checks if string follows one of the most popular programming naming convention. |
isTrimmed(side: TrimmedSide) | Checks if string does not contain any leading and trailing whitespace. |
isAlphaNumeric() | Checks if string only contains alpha characters and/or numbers. |
isAlpha(alphabet?: Alphabet) | Checks if string only contains alpha characters. |
isNumeric() | Checks if string only contains numbers. |
isAscii() | Checks if string only contains printable ASCII characters. |
isBinary() | Checks if string is a binary number (base-2). |
isBoolean() | Checks if string is a boolean string. |
isOctal() | Checks if string is an octal number (base-8). |
isHex() | Checks if string is a hexadecimal number (base-16). |
isBase64(impl: Base64Implementation) | Checks if string is Base64 encoded. |
isJson(format?: JsonFormat) | Checks if string is a valid JSON string. |
isDecimal(options?: IIsDecimalOptions) | Checks if string is a decimal number. |
isEmailAddress(def?: EmailAddressDefinition) | Checks if string is an email address. |
isObjectId() | Checks if string is a representation of a MongoDB ObjectId. |
isHexColor(digits?: HexColorDigits) | Checks if string is a hexadecimal color. |
isUuid(version?: UuidVersion) | Checks if string is an Universally unique identifier. |
isMacAddress(def?: MacAddressDefinition) | Checks if string is a MAC address. |
isIpAddress(version?: IpVersion) | Checks if string is an IP address. |
isLatitude(format?: GeoCoordinatesFormat) | Checks if string is a latitude geographic coordinate. |
isLongitude(format?: GeoCoordinatesFormat) | Checks if string is a longitude geographic coordinate. |
isLatLong(format?: GeoCoordinatesFormat) | Checks if string is a latitude-longitude geographic coordinate. |
isIso639Part1Alpha2() | Checks if string is an ISO 639-1 alpha-2 language code. |
isIso639Part2Alpha3(set?: Iso639Part2Alpha3Set) | Checks if string is an ISO 639-2 alpha-3 language code. |
isIso3166Part1Alpha(version?: Iso3166Part1AlphaVersion) | Checks if string is an ISO 3166-1 alpha country code. |
isIso4217Alpha3() | Checks if string is an ISO 4217 alpha-3 currency code. |
Adds one GuardResult
instance in bulk:
const guardResultBulk = new GuardResultBulk().add(Tyr.string().equals('foo').guard('foo'));
Adds multiple GuardResult
instances in bulk:
const guardResultBulk = new GuardResultBulk()
.add([
Tyr.string().equals('foo').guard('foo'),
Tyr.string().hasMinLength(4).guard('bar'),
])
Returns either the first fail in bulk, or only one success:
const guardResult = new GuardResultBulk()
.add([
Tyr.string().equals('foo').guard('foo'),
Tyr.string().hasMinLength(4).guard('bar'),
Tyr.number().isBetween(10, 20).isEven().guard(14),
])
.combine();
console.log(guardResult);
// => GuardResult {
// success: false,
// propertyName: undefined,
// message: 'string is expected to have min length of 4 but has length of: 3'
// }
const guardResult = new GuardResultBulk()
.add([
Tyr.string().equals('foo').guard('foo'),
Tyr.string().hasMinLength(3).guard('bar'),
Tyr.number().isBetween(10, 20).isEven().guard(14),
])
.combine();
console.log(guardResult);
// => GuardResult { success: true }
Returns all fails in bulk, or only one success:
const guardResults = new GuardResultBulk()
.add([
Tyr.array().hasMinSize(2).contains('foo').guard(['foo', 'bar']),
Tyr.string().equals('foo').guard('bar'),
Tyr.number().isBetween(10, 20).isEven().guard(15),
])
.stack();
console.log(guardResults);
// => [
// GuardResult {
// success: false,
// propertyName: undefined,
// message: 'string is expected to be equal to foo but is not: bar'
// },
// GuardResult {
// success: false,
// propertyName: undefined,
// message: 'number is expected to be even but is not: 15'
// }
// ]
const guardResults = new GuardResultBulk()
.add([
Tyr.array().hasMinSize(2).contains('foo').guard(['foo', 'bar']),
Tyr.string().equals('foo').guard('foo'),
Tyr.number().isBetween(10, 20).isEven().guard(14),
])
.stack();
console.log(guardResults);
// => [ GuardResult { success: true } ]
Refer to the Codex to directly access Enums containing some ISO values:
Codex entry | Description | Returns |
---|---|---|
Codex.iso3166Part1Alpha2Codes() | List of ISO 3166-1 alpha-2 country codes. | Iso4217Alpha3[] |
Codex.iso3166Part1Alpha3Codes() | List of ISO 3166-1 alpha-3 country codes. | Iso639Part1Alpha2[] |
Codex.iso4217Alpha3Codes() | List of active ISO 4217 alpha-3 currency codes. | Iso639Part2Alpha3[] |
Codex.iso639Part1Alpha2Codes() | List of 184 ISO 639-1 alpha-2 language codes. | Iso3166Part1Alpha2[] |
Codex.iso639Part2Alpha3Codes(set?: Iso639Part2Alpha3Set) | List of 487 ISO 639-2 alpha-3 language codes. | Iso639Part2Alpha3B[] | Iso639Part2Alpha3T[] | Iso639Part2Alpha3[] |
See information about breaking changes and release notes here.
Once upon a time there was a forgotten kingdom, lost in the far north, called TypGard 🌈.
This kingdom was populated by two complementary tribes living in harmony: the Backings and the Frontkings. Two other tribes, constantly at war, the Vikings and the Nanokings quarreled in the mountains 🗻, but we will not discuss them in this story ...
The Backings were a people of craftsmen: miners 💎, blacksmiths 🔨, loggers, lumberjacks. The Frontkings were a tribe skilled in the arts, including body painting 🎭 and storytelling 🎻.
After each daily thing ☕, Frontking's skalds often swapped the tale of one or two sagas against the essential goods produced by the Backings.
And all this little small world lived in cooperation and peace, until the day, when curious foreigners from the south, tried to sow trouble, and divide this beautiful and well-ordered kingdom 🐍. The northerners, not understanding their language, decided to simply name these invaders the sons of Julius, or Jsøns in Old Nordic language.
The Jsøns were devious and manipulative, and the Northerners never knew if they were telling them the truth .
Fortunately, they could rely on Tyr, the deity of order and law, to gauge each suspected Jsøn, and validate his claims.
Tyr was accompanied by seven guards, each representing an aspect of the order ...
- xtitusx - Benjamin Tussac (author)
MIT License
Copyright (c) 2021 Benjamin Tussac
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.