From c26a50c0cbe7e189a6433dab4fb276b56433096e Mon Sep 17 00:00:00 2001 From: Henri Cazottes Date: Wed, 31 May 2023 17:43:40 +0200 Subject: [PATCH] feat: add StringConstraints (#20) * feat(iftools_287): add IsPopulatedString * feat(iftools_287): add contraints to IsString * feat(iftools_287): rename to IsFilledString * feat(itools_287): make use of IsString constraints * feat(itools_287): improve tests --- README.md | 31 ++++++++- __tests__/TypeAssertion.spec.ts | 111 +++++++++++++++++++++++++++++++- __tests__/TypeGuard.spec.ts | 67 +++++++++++++++++++ src/TypeAssertion.ts | 42 +++++++++++- src/TypeGuard.ts | 50 +++++++++++++- src/Types.ts | 17 ++++- 6 files changed, 310 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c52ec64..4661b6a 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,32 @@ Asserts that the value is a number, but not NaN nor +/-Infinity. ### IsString ```ts -static IsString(value: unknown): void +static IsString(value: unknown, constraints?: StringConstraints): void ``` Asserts that the value is a string. +The optional parameter `constraints` accept an object described by the following interface. + +```ts +interface StringConstraints +{ + minLength?: number; + maxLength?: number; +} +``` + +If `minLength` is provided, it'll asserts that the value has at least this length.
+If `maxLength` is provided, it'll asserts that the value has at most this length. + +### IsFilledString + +```ts +static IsFilledString(value: unknown): void +``` + +Like `IsString`, but asserts that the string is never empty too. + ### IsArray ```ts @@ -211,6 +232,14 @@ static IsString(value: unknown): boolean Narrow down the value to being a string. +### IsFilledString + +```ts +static IsFilledString(value: unknown): void +``` + +Asserts that the value is a non empty string. + ### IsArray ```ts diff --git a/__tests__/TypeAssertion.spec.ts b/__tests__/TypeAssertion.spec.ts index 9b29c6b..398c814 100644 --- a/__tests__/TypeAssertion.spec.ts +++ b/__tests__/TypeAssertion.spec.ts @@ -298,6 +298,115 @@ describe( } } ); + + it( + "should return when given a string with a length greater or equal to the minLength constraint", + (): void => + { + expect( + (): void => + { + TypeAssertion.IsString("Allan", { minLength: 1 }); + } + ).to.not.throw(); + + expect( + (): void => + { + TypeAssertion.IsString("Allan", { minLength: 5 }); + } + ).to.not.throw(); + } + ); + + it( + "should throw when given a string with a length shorter than minLength constraint", + (): void => + { + expect( + (): void => + { + TypeAssertion.IsString("Allan", { minLength: 6 }); + } + ).to.throw("value length is shorter than minimum length 6"); + } + ); + + it( + "should return when given a string with a length shorter or equal to the maxLength constraint", + (): void => + { + expect( + (): void => + { + TypeAssertion.IsString("Allan", { maxLength: 10 }); + } + ).to.not.throw(); + + expect( + (): void => + { + TypeAssertion.IsString("Allan", { maxLength: 5 }); + } + ).to.not.throw(); + } + ); + + it( + "should throw when given a string with a length greater than maxLength constraint", + (): void => + { + expect( + (): void => + { + TypeAssertion.IsString("Allan", { maxLength: 4 }); + } + ).to.throw("value length is greater than maximum length 4"); + } + ); + } + ); + + describe( + "IsFilledString", + (): void => + { + it( + "should return when given a non empty string", + (): void => + { + const VALUES: Array = getValues(BaseType.STRING) + .filter((value) => { return value !== ""; }); + + for (const ITEM of VALUES) + { + expect( + (): void => + { + TypeAssertion.IsFilledString(ITEM); + } + ).to.not.throw(); + } + } + ); + + it( + "should throw when given anything else", + (): void => + { + const VALUES: Array = [...getInvertedValues(BaseType.STRING), ""]; + + for (const ITEM of VALUES) + { + expect( + (): void => + { + TypeAssertion.IsFilledString(ITEM); + } + ).to.throw(/^value /); + } + } + ); } ); @@ -434,7 +543,7 @@ describe( ); it( - "should throw when given a populated array", + "should return when given a populated array", (): void => { expect( diff --git a/__tests__/TypeGuard.spec.ts b/__tests__/TypeGuard.spec.ts index bd19139..251f003 100644 --- a/__tests__/TypeGuard.spec.ts +++ b/__tests__/TypeGuard.spec.ts @@ -260,6 +260,73 @@ describe( } } ); + + it( + "should return true when given a string with a length greater or equal to the minLength constraint", + (): void => + { + expect(TypeGuard.IsString("Allan", { minLength: 2 })).to.be.true; + expect(TypeGuard.IsString("Allan", { minLength: 5 })).to.be.true; + } + ); + + it( + "should return false when given an array with a length below the minLength constraint", + (): void => + { + expect(TypeGuard.IsString("Allan", { minLength: 6 })).to.be.false; + } + ); + + it( + "should return true when given a string with a length shorter or equal to the maxLength constraint", + (): void => + { + expect(TypeGuard.IsString("Allan", { maxLength: 5 })).to.be.true; + expect(TypeGuard.IsString("Allan", { maxLength: 10 })).to.be.true; + } + ); + + it( + "should return false when given a string with a length above the maxLength constraint", + (): void => + { + expect(TypeGuard.IsString("Allan", { maxLength: 2 })).to.be.false; + } + ); + } + ); + + describe( + "IsFilledString", + (): void => + { + it( + "should return true when given a populated string", + (): void => + { + const VALUES: Array = getValues(BaseType.STRING) + .filter((value) => { return value !== ""; }); + + for (const ITEM of VALUES) + { + expect(TypeGuard.IsFilledString(ITEM)).to.be.true; + } + } + ); + + it( + "should return false when given anything else", + (): void => + { + const VALUES: Array = [...getInvertedValues(BaseType.STRING), ""]; + + for (const ITEM of VALUES) + { + expect(TypeGuard.IsFilledString(ITEM)).to.be.false; + } + } + ); } ); diff --git a/src/TypeAssertion.ts b/src/TypeAssertion.ts index 90dcbf9..5a9527d 100644 --- a/src/TypeAssertion.ts +++ b/src/TypeAssertion.ts @@ -1,6 +1,6 @@ import { TypeGuard } from "./TypeGuard.js"; -import type { ArrayConstraints, ObjectWithNullableProperty, ObjectWithProperty, PopulatedArray } from "./Types.js"; +import type { ArrayConstraints, ObjectWithNullableProperty, ObjectWithProperty, PopulatedArray, StringConstraints } from "./Types.js"; /** * TypeAssertion @@ -110,15 +110,53 @@ class TypeAssertion * @public * @static * @param {unknown} value the value + * @param {object} [constraints] some additional check + * @param {number} [constraints.minLength] minimal length + * @param {number} [constraints.maxLength] maximum length * @throws {Error} if the value is not a string * @return {void} nothing */ - public static IsString(value: unknown): asserts value is string + public static IsString(value: unknown, constraints?: StringConstraints): asserts value is string { if (!TypeGuard.IsString(value)) { throw new Error("value is not a string"); } + + if (constraints === undefined) + { + return; + } + + if (constraints.minLength !== undefined && value.length < constraints.minLength) + { + throw new Error(`value length is shorter than minimum length ${constraints.minLength.toString()}`); + } + + if (constraints.maxLength !== undefined && value.length > constraints.maxLength) + { + throw new Error(`value length is greater than maximum length ${constraints.maxLength.toString()}`); + } + } + + /** + * IsFilledString + * + * @description Assertion that check if a value is non empty string. + * @public + * @static + * @param {unknown} value the value + * @throws {Error} if the value is not a string + * @return {void} nothing + */ + public static IsFilledString(value: unknown): asserts value is string + { + TypeAssertion.IsString(value, { + /** + * + */ + minLength: 1, + }); } /** diff --git a/src/TypeGuard.ts b/src/TypeGuard.ts index a1ec01f..78e1a54 100644 --- a/src/TypeGuard.ts +++ b/src/TypeGuard.ts @@ -1,4 +1,4 @@ -import type { ArrayConstraints, ObjectWithNullableProperty, ObjectWithProperty, PopulatedArray } from "./Types.js"; +import type { ArrayConstraints, ObjectWithNullableProperty, ObjectWithProperty, PopulatedArray, StringConstraints } from "./Types.js"; /** * TypeGuard @@ -108,11 +108,55 @@ class TypeGuard * @public * @static * @param {unknown} value the value + * @param {object} [constraints] some additional check + * @param {number} [constraints.minLength] minimal length + * @param {number} [constraints.maxLength] maximum length + * @return {boolean} a boolean + */ + public static IsString(value: unknown, constraints?: StringConstraints): value is string + { + if (typeof value !== "string") + { + return false; + } + + if (constraints === undefined) + { + return true; + } + + if (constraints.minLength !== undefined && value.length < constraints.minLength) + { + return false; + } + + if (constraints.maxLength !== undefined && value.length > constraints.maxLength) + { + return false; + } + + return true; + } + + /** + * IsFilledString + * + * @description Predicate that check if a value is a non empty string. + * @public + * @static + * @param {unknown} value the value * @return {boolean} a boolean */ - public static IsString(value: unknown): value is string + public static IsFilledString( + value: unknown, + ): value is string { - return typeof value === "string"; + return TypeGuard.IsString(value, { + /** + * + */ + minLength: 1 + }); } /** diff --git a/src/Types.ts b/src/Types.ts index 7ff8ea0..927ff4b 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -32,4 +32,19 @@ interface ArrayConstraints itemGuard?: (item: unknown) => item is Type; } -export type { ArrayConstraints, ObjectWithNullableProperty, ObjectWithProperty, PopulatedArray }; +/** + * StringConstraints interface + */ +interface StringConstraints +{ + /** + * minLength + */ + minLength?: number; + /** + * maxLength + */ + maxLength?: number; +} + +export type { ArrayConstraints, ObjectWithNullableProperty, ObjectWithProperty, PopulatedArray, StringConstraints };