diff --git a/.mocharc.cjs b/.mocharc.cjs index d733ac2..8857bfb 100644 --- a/.mocharc.cjs +++ b/.mocharc.cjs @@ -14,4 +14,5 @@ module.exports = { exclude: [ "**/*.d.mts" ] + // slow: 0 }; \ No newline at end of file diff --git a/README.md b/README.md index 35739ff..3afce1e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ [![npm version](https://badge.fury.io/js/%40cowwoc%2Frequirements.svg)](https://badge.fury.io/js/%40cowwoc%2Frequirements) [![build-status](https://github.com/cowwoc/requirements.js/workflows/Build/badge.svg)](https://github.com/cowwoc/requirements.js/actions?query=workflow%3ABuild) -# checklist Fluent API for Design Contracts +# checklist Fluent API for Design Contracts -[![API](https://img.shields.io/badge/api_docs-5B45D5.svg)](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/) +[![API](https://img.shields.io/badge/api_docs-5B45D5.svg)](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/) [![Changelog](https://img.shields.io/badge/changelog-A345D5.svg)](wiki/Changelog.md) [![java](https://img.shields.io/badge/other%20languages-java-457FD5.svg)](../../../requirements.java) @@ -17,13 +17,13 @@ A [fluent API](https://en.wikipedia.org/wiki/Fluent_interface) for enforcing To get started, add this dependency: ```shell -npm install --save @cowwoc/requirements@3.2.1 +npm install --save @cowwoc/requirements@3.2.2 ``` or [pnpm](https://pnpm.io/): ```shell -pnpm add @cowwoc/requirements@3.2.1 +pnpm add @cowwoc/requirements@3.2.2 ``` The contents of the API classes depend on which [modules](wiki/Supported_Libraries.md) are enabled. @@ -67,13 +67,13 @@ Actual: 15 The best way to learn about the API is using your IDE's auto-complete engine. There are six entry points you can navigate from: -* [requireThat(value, name)](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-DefaultRequirements.html#~requireThat) -* [validateThat(value, name)](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-DefaultRequirements.html#~validateThat) -* [assertThat(Function)](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-DefaultRequirements.html#~assertThat) -* [assertThatAndReturn(Function)](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-DefaultRequirements.html#~assertThatAndReturn) +* [requireThat(value, name)](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-DefaultRequirements.html#~requireThat) +* [validateThat(value, name)](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-DefaultRequirements.html#~validateThat) +* [assertThat(Function)](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-DefaultRequirements.html#~assertThat) +* [assertThatAndReturn(Function)](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-DefaultRequirements.html#~assertThatAndReturn) -* [Requirements](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-Requirements-Requirements.html) -* [GlobalRequirements](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-GlobalRequirements-GlobalRequirements.html) +* [Requirements](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-Requirements-Requirements.html) +* [GlobalRequirements](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-GlobalRequirements-GlobalRequirements.html) ## Best practices diff --git a/build.mts b/build.mts index 90de90e..2f9b082 100644 --- a/build.mts +++ b/build.mts @@ -84,8 +84,10 @@ class Build // tsconfig.json references the tests to suppress an ESLint warning, but we don't actually want to publish // them. - const index = config.include.indexOf("test/**/*.mts", 0); - config.include.splice(index, 1); + config.include = config.include.filter((element: string) => + { + return element !== "build.mts" && !element.startsWith("test/"); + }); config.compilerOptions.outDir = "target/publish/node/"; config.compilerOptions.declaration = true; diff --git a/package.json b/package.json index c2b500f..0ce6655 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cowwoc/requirements", - "version": "3.2.1", + "version": "3.2.2", "keywords": [ "preconditions", "postconditions", diff --git a/src/ArrayValidator.mts b/src/ArrayValidator.mts index 2efd683..ffd943f 100644 --- a/src/ArrayValidator.mts +++ b/src/ArrayValidator.mts @@ -13,22 +13,24 @@ import type { * exceptions. * * All methods (except those found in {@link ObjectValidator}) imply {@link isNotNull}. + * + * @typeParam E - the type the array elements */ -interface ArrayValidator extends ExtensibleObjectValidator +interface ArrayValidator extends ExtensibleObjectValidator, E[]> { /** * Ensures that the actual value is empty. * * @returns the updated validator */ - isEmpty(): ArrayValidator; + isEmpty(): ArrayValidator; /** * Ensures that the actual value is not empty. * * @returns the updated validator */ - isNotEmpty(): ArrayValidator; + isNotEmpty(): ArrayValidator; /** * Ensures that the array contains an element. @@ -39,7 +41,7 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - contains(element: unknown, name?: string): ArrayValidator; + contains(element: E, name?: string): ArrayValidator; /** * Ensures that the array contains exactly the specified elements; nothing less, nothing more. @@ -50,7 +52,7 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - containsExactly(expected: unknown[], name?: string): ArrayValidator; + containsExactly(expected: E[], name?: string): ArrayValidator; /** * Ensures that the array contains any of the specified elements. @@ -61,7 +63,7 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - containsAny(expected: unknown[], name?: string): ArrayValidator; + containsAny(expected: E[], name?: string): ArrayValidator; /** * Ensures that the array contains all the specified elements. @@ -72,7 +74,7 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - containsAll(expected: unknown[], name?: string): ArrayValidator; + containsAll(expected: E[], name?: string): ArrayValidator; /** * Ensures that the array does not contain an element. @@ -83,7 +85,7 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - doesNotContain(element: unknown, name?: string): ArrayValidator; + doesNotContain(element: E, name?: string): ArrayValidator; /** * Ensures that the array does not contain any of the specified elements. @@ -94,7 +96,7 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - doesNotContainAny(elements: unknown[], name?: string): ArrayValidator; + doesNotContainAny(elements: E[], name?: string): ArrayValidator; /** * Ensures that the array does not contain all the specified elements. @@ -105,14 +107,14 @@ interface ArrayValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - doesNotContainAll(elements: unknown[], name?: string): ArrayValidator; + doesNotContainAll(elements: E[], name?: string): ArrayValidator; /** * Ensures that the array does not contain any duplicate elements. * * @returns the updated validator */ - doesNotContainDuplicates(): ArrayValidator; + doesNotContainDuplicates(): ArrayValidator; /** * @returns a validator for the length of the array @@ -124,23 +126,44 @@ interface ArrayValidator extends ExtensibleObjectValidator * @returns the updated validator * @throws TypeError if consumer is not set */ - lengthConsumer(consumer: (length: NumberValidator) => void): ArrayValidator; + lengthConsumer(consumer: (length: NumberValidator) => void): ArrayValidator; + + /** + * @returns a validator for the Array + * @deprecated returns this + */ + asArray(): ArrayValidator; + + asArray(): ArrayValidator; + + /** + * @param consumer - a function that accepts a {@link ArrayValidator} for the actual value + * @returns the updated validator + * @throws TypeError if consumer is not set + */ + asArrayConsumer(consumer: (input: ArrayValidator) => void): ArrayValidator; + + asArrayConsumer(consumer: (input: ArrayValidator) => void): ArrayValidator; /** * Verifies the Set representation of the array. * * @returns a Set validator */ - asSet(): SetValidator; + asSet(): SetValidator; + + asSet(): SetValidator; /** * @param consumer - a function that accepts a {@link SetValidator} for the Set representation of the array * @returns the updated validator * @throws TypeError if consumer is not set */ - asSetConsumer(consumer: (actual: SetValidator) => void): ArrayValidator; + asSetConsumer(consumer: (actual: SetValidator) => void): ArrayValidator; + + asSetConsumer(consumer: (actual: SetValidator) => void): ArrayValidator; - getActual(): unknown[] | void; + getActual(): E[] | undefined; } export {type ArrayValidator}; \ No newline at end of file diff --git a/src/ArrayVerifier.mts b/src/ArrayVerifier.mts index f422bc7..ad38730 100644 --- a/src/ArrayVerifier.mts +++ b/src/ArrayVerifier.mts @@ -8,8 +8,10 @@ import type { * Verifies the requirements of an array. * * All methods (except those found in {@link ObjectVerifier}) imply {@link isNotNull}. + * + * @typeParam E - the type the array elements */ -interface ArrayVerifier extends ExtensibleObjectVerifier +interface ArrayVerifier extends ExtensibleObjectVerifier, E[]> { /** * Ensures that the actual value is empty. @@ -17,7 +19,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @returns the updated verifier * @throws RangeError if the actual value is not empty */ - isEmpty(): ArrayVerifier; + isEmpty(): ArrayVerifier; /** * Ensures that the actual value is not empty. @@ -25,7 +27,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @returns the updated verifier * @throws RangeError if the actual value is empty */ - isNotEmpty(): ArrayVerifier; + isNotEmpty(): ArrayVerifier; /** * Ensures that the array contains an element. @@ -37,7 +39,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the array does not contain element. */ - contains(element: unknown, name?: string): ArrayVerifier; + contains(element: E, name?: string): ArrayVerifier; /** * Ensures that the array contains exactly the specified elements; nothing less, nothing more. @@ -51,7 +53,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * If the array is missing any elements in expected. * If the array contains elements not found in expected. */ - containsExactly(expected: unknown[], name?: string): ArrayVerifier; + containsExactly(expected: E[], name?: string): ArrayVerifier; /** * Ensures that the array contains any of the specified elements. @@ -65,7 +67,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * If the array is missing any elements in expected. * If the array contains elements not found in expected. */ - containsAny(expected: unknown[], name?: string): ArrayVerifier; + containsAny(expected: E[], name?: string): ArrayVerifier; /** * Ensures that the array contains all the specified elements. @@ -78,7 +80,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the array does not contain all of expected. */ - containsAll(expected: unknown[], name?: string): ArrayVerifier; + containsAll(expected: E[], name?: string): ArrayVerifier; /** * Ensures that the array does not contain an element. @@ -90,7 +92,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the array contains element. */ - doesNotContain(element: unknown, name?: string): ArrayVerifier; + doesNotContain(element: E, name?: string): ArrayVerifier; /** * Ensures that the array does not contain any of the specified elements. @@ -103,7 +105,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the array contains any of elements. */ - doesNotContainAny(elements: unknown[], name?: string): ArrayVerifier; + doesNotContainAny(elements: E[], name?: string): ArrayVerifier; /** * Ensures that the array does not contain all the specified elements. @@ -116,7 +118,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the array contains all of elements. */ - doesNotContainAll(elements: unknown[], name?: string): ArrayVerifier; + doesNotContainAll(elements: E[], name?: string): ArrayVerifier; /** * Ensures that the array does not contain any duplicate elements. @@ -124,7 +126,7 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @returns the updated verifier * @throws RangeError if the array contains any duplicate elements */ - doesNotContainDuplicates(): ArrayVerifier; + doesNotContainDuplicates(): ArrayVerifier; /** * @returns a verifier for the length of the array @@ -136,23 +138,46 @@ interface ArrayVerifier extends ExtensibleObjectVerifier * @returns the updated verifier * @throws TypeError if consumer is not set */ - lengthConsumer(consumer: (actual: NumberVerifier) => void): ArrayVerifier; + lengthConsumer(consumer: (actual: NumberVerifier) => void): ArrayVerifier; + + /** + * @returns a verifier for the Array + * @throws TypeError if the actual value is not an Array + * @deprecated returns this + */ + asArray(): ArrayVerifier; + + asArray(): ArrayVerifier; + + /** + * @param consumer - a function that accepts a {@link ArrayVerifier} for the actual value + * @returns the updated verifier + * @throws TypeError if consumer is not set. + * If the actual value is not an Array. + */ + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): ArrayVerifier; + + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): ArrayVerifier; /** * Verifies the Set representation of the array. * * @returns a Set verifier */ - asSet(): SetVerifier; + asSet(): SetVerifier; + + asSet(): SetVerifier; /** * @param consumer - a function that accepts a {@link SetVerifier} for the Set representation of the array * @returns the updated verifier * @throws TypeError if consumer is not set */ - asSetConsumer(consumer: (actual: SetVerifier) => void): ArrayVerifier; + asSetConsumer(consumer: (actual: SetVerifier) => void): ArrayVerifier; + + asSetConsumer(consumer: (actual: SetVerifier) => void): ArrayVerifier; - getActual(): unknown[]; + getActual(): E[]; } export {type ArrayVerifier}; \ No newline at end of file diff --git a/src/BooleanValidator.mts b/src/BooleanValidator.mts index 2fd4839..158e899 100644 --- a/src/BooleanValidator.mts +++ b/src/BooleanValidator.mts @@ -10,7 +10,7 @@ import type {ExtensibleObjectValidator} from "./internal/internal.mjs"; * * All methods (except those found in {@link ObjectValidator}) imply {@link isNotNull}. */ -interface BooleanValidator extends ExtensibleObjectValidator +interface BooleanValidator extends ExtensibleObjectValidator { /** * Ensures that the actual value is true. @@ -26,7 +26,7 @@ interface BooleanValidator extends ExtensibleObjectValidator */ isFalse(): BooleanValidator; - getActual(): boolean | void; + getActual(): boolean | undefined; } export {type BooleanValidator}; \ No newline at end of file diff --git a/src/BooleanVerifier.mts b/src/BooleanVerifier.mts index d735a50..ca7fa83 100644 --- a/src/BooleanVerifier.mts +++ b/src/BooleanVerifier.mts @@ -5,7 +5,7 @@ import type {ExtensibleObjectVerifier} from "./internal/internal.mjs"; *

* All methods (except those found in {@link ObjectVerifier}) imply {@link isNotNull}. */ -interface BooleanVerifier extends ExtensibleObjectVerifier +interface BooleanVerifier extends ExtensibleObjectVerifier { /** * Ensures that the actual value is true. diff --git a/src/ClassValidator.mts b/src/ClassValidator.mts index 1e525a4..c147b06 100644 --- a/src/ClassValidator.mts +++ b/src/ClassValidator.mts @@ -10,7 +10,8 @@ import type {ExtensibleObjectValidator} from "./internal/internal.mjs"; * * All methods (except those found in {@link ObjectValidator}) imply {@link isNotNull}. */ -interface ClassValidator extends ExtensibleObjectValidator +// eslint-disable-next-line @typescript-eslint/ban-types +interface ClassValidator extends ExtensibleObjectValidator { /** * Ensures that the actual value is the specified type, or a subtype. @@ -31,7 +32,7 @@ interface ClassValidator extends ExtensibleObjectValidator isSubtypeOf(type: Function): ClassValidator; // eslint-disable-next-line @typescript-eslint/ban-types - getActual(): Function | void; + getActual(): Function | undefined; } export {type ClassValidator}; \ No newline at end of file diff --git a/src/ClassVerifier.mts b/src/ClassVerifier.mts index 6896aa5..821a141 100644 --- a/src/ClassVerifier.mts +++ b/src/ClassVerifier.mts @@ -5,7 +5,8 @@ import type {ObjectVerifier} from "./internal/internal.mjs"; *

* All methods (except those found in {@link ObjectVerifier}) imply {@link isNotNull}. */ -interface ClassVerifier extends ObjectVerifier +// eslint-disable-next-line @typescript-eslint/ban-types +interface ClassVerifier extends ObjectVerifier { /** * Ensures that the actual value is the specified type, or a super-type. diff --git a/src/DefaultRequirements.mts b/src/DefaultRequirements.mts index c44a36f..4c817c3 100644 --- a/src/DefaultRequirements.mts +++ b/src/DefaultRequirements.mts @@ -1,46 +1,120 @@ import type { GlobalConfiguration, ObjectValidator, - ObjectVerifier + ObjectVerifier, + StringValidator, + NumberValidator, + ClassValidator, + ArrayValidator, + SetValidator, + MapValidator, + StringVerifier, + NumberVerifier, + ClassVerifier, + ArrayVerifier, + SetVerifier, + MapVerifier +} from "./internal/internal.mjs"; +import { + Requirements, + Objects, + ArrayValidatorImpl, + Pluralizer, + SetValidatorImpl, + ClassValidatorImpl, + ObjectValidatorImpl, + Configuration, + MainGlobalConfiguration, + ArrayVerifierImpl, + SetVerifierImpl, + MapVerifierImpl, + MapValidatorImpl, + ClassVerifierImpl, + ObjectVerifierImpl } from "./internal/internal.mjs"; -import {Requirements} from "./internal/internal.mjs"; const typedocWorkaround: null | GlobalConfiguration = null; // noinspection PointlessBooleanExpressionJS if (typedocWorkaround !== null) console.log("WORKAROUND: https://github.com/microsoft/tsdoc/issues/348"); -const delegate = new Requirements(); - /** * Verifies the requirements of an object. * + * @typeParam T - the type the actual value * @param actual - the actual value * @param name - the name of the value * @returns a verifier * @throws TypeError if name is null * @throws RangeError if name is empty */ -function requireThat(actual: unknown, name: string): ObjectVerifier +function requireThat(actual: string, name: string): StringVerifier; +function requireThat(actual: number, name: string): NumberVerifier; +function requireThat(actual: null, name: string): ObjectVerifier; +// eslint-disable-next-line @typescript-eslint/ban-types +function requireThat(actual: Function, name: string): ClassVerifier; +function requireThat(actual: Array, name: string): ArrayVerifier; +function requireThat(actual: Set, name: string): SetVerifier; +function requireThat(actual: Map, name: string): MapVerifier; +function requireThat(actual: T, name: string): ObjectVerifier; +// eslint-disable-next-line @typescript-eslint/ban-types +function requireThat | Set | Map, E, K, V> +(actual: T, name: string): StringVerifier | NumberVerifier | ClassVerifier | ArrayVerifier | SetVerifier | MapVerifier | ObjectVerifier { - return delegate.requireThat(actual, name); + Objects.verifyName(name, "name"); + const config = new Configuration(MainGlobalConfiguration.INSTANCE); + const typeOfActual = Objects.getTypeInfo(actual); + if (typeOfActual.type === "array") + return new ArrayVerifierImpl(new ArrayValidatorImpl(config, actual as E[], name, Pluralizer.ELEMENT, [])); + if (typeOfActual.type === "object" && typeOfActual.name === "Set") + return new SetVerifierImpl(new SetValidatorImpl(config, actual as Set, name, [])); + if (typeOfActual.type === "object" && typeOfActual.name === "Map") + return new MapVerifierImpl(new MapValidatorImpl(config, actual as Map, name, [])); + if (typeOfActual.type === "class") + { + // eslint-disable-next-line @typescript-eslint/ban-types + return new ClassVerifierImpl(new ClassValidatorImpl(config, actual as Function, name, [])); + } + return new ObjectVerifierImpl(new ObjectValidatorImpl(config, actual, name, [])); } /** - * Verifies requirements only if assertions are enabled. - * - * By default, assertions are disabled. - * See {@link GlobalConfiguration.assertionsAreEnabled | GlobalConfiguration.assertionsAreEnabled} to change - * the default. + * Validates the requirements of an object. * - * @param requirements - the requirements to verify + * @typeParam T - the type the actual value + * @param actual - the actual value + * @param name - the name of the value + * @returns a validator * @throws TypeError if name is null * @throws RangeError if name is empty * @see {@link GlobalConfiguration.assertionsAreEnabled | GlobalConfiguration.assertionsAreEnabled} */ -function assertThat(requirements: (requirements: Requirements) => void) +function validateThat(actual: string, name: string): StringValidator; +function validateThat(actual: number, name: string): NumberValidator; +function validateThat(actual: null, name: string): ObjectValidator; +// eslint-disable-next-line @typescript-eslint/ban-types +function validateThat(actual: Function, name: string): ClassValidator; +function validateThat(actual: Array, name: string): ArrayValidator; +function validateThat(actual: Set, name: string): SetValidator; +function validateThat(actual: Map, name: string): MapValidator; +function validateThat(actual: T, name: string): ObjectValidator; +// eslint-disable-next-line @typescript-eslint/ban-types +function validateThat | Set | Map, E, K, V> +(actual: T, name: string): StringValidator | NumberValidator | ClassValidator | ArrayValidator | SetValidator | MapValidator | ObjectValidator { - delegate.assertThat(requirements); + Objects.verifyName(name, "name"); + const config = new Configuration(MainGlobalConfiguration.INSTANCE); + const typeOfActual = Objects.getTypeInfo(actual); + if (typeOfActual.type === "array") + return new ArrayValidatorImpl(config, actual as E[], name, Pluralizer.ELEMENT, []); + if (typeOfActual.type === "object" && typeOfActual.name === "Set") + return new SetValidatorImpl(config, actual as Set, name, []); + if (typeOfActual.type === "class") + { + // eslint-disable-next-line @typescript-eslint/ban-types + return new ClassValidatorImpl(config, actual as Function, name, []); + } + return new ObjectValidatorImpl(config, actual, name, []); } /** @@ -51,35 +125,44 @@ function assertThat(requirements: (requirements: Requirements) => void) * the default. * * @param requirements - the requirements to verify - * @returns the value returned by requirements * @throws TypeError if name is null * @throws RangeError if name is empty * @see {@link GlobalConfiguration.assertionsAreEnabled | GlobalConfiguration.assertionsAreEnabled} */ -function assertThatAndReturn(requirements: (requirements: Requirements) => void) +function assertThat(requirements: (requirements: Requirements) => void) { - return delegate.assertThatAndReturn(requirements); + Objects.requireThatValueIsDefinedAndNotNull(requirements, "requirements"); + const config = new Configuration(MainGlobalConfiguration.INSTANCE); + if (config.assertionsAreEnabled()) + requirements(new Requirements()); } /** - * Validates the requirements of an object. + * Verifies requirements only if assertions are enabled. * - * @param actual - the actual value - * @param name - the name of the value - * @returns a validator + * By default, assertions are disabled. + * See {@link GlobalConfiguration.assertionsAreEnabled | GlobalConfiguration.assertionsAreEnabled} to change + * the default. + * + * @param requirements - the requirements to verify + * @returns the value returned by requirements * @throws TypeError if name is null * @throws RangeError if name is empty * @see {@link GlobalConfiguration.assertionsAreEnabled | GlobalConfiguration.assertionsAreEnabled} */ -function validateThat(actual: unknown, name: string): ObjectValidator +function assertThatAndReturn(requirements: (requirements: Requirements) => void) { - return delegate.validateThat(actual, name); + Objects.requireThatValueIsDefinedAndNotNull(requirements, "requirements"); + const config = new Configuration(MainGlobalConfiguration.INSTANCE); + if (config.assertionsAreEnabled()) + return requirements(new Requirements()); + return undefined; } export { requireThat, + validateThat, assertThat, - assertThatAndReturn, - validateThat + assertThatAndReturn }; \ No newline at end of file diff --git a/src/InetAddressValidator.mts b/src/InetAddressValidator.mts index ffd1bdc..c703cc6 100644 --- a/src/InetAddressValidator.mts +++ b/src/InetAddressValidator.mts @@ -10,7 +10,7 @@ import type {ExtensibleObjectValidator} from "./internal/internal.mjs"; * * All methods (except those found in {@link ObjectValidator}) imply {@link isNotNull}. */ -interface InetAddressValidator extends ExtensibleObjectValidator +interface InetAddressValidator extends ExtensibleObjectValidator { /** * Ensures that the actual value is an IP v4 address. @@ -34,7 +34,7 @@ interface InetAddressValidator extends ExtensibleObjectValidator * All methods (except those found in {@link ObjectVerifier}) imply {@link isNotNull}. */ -interface InetAddressVerifier extends ObjectVerifier +interface InetAddressVerifier extends ObjectVerifier { /** * Ensures that the actual value is an IP v4 address. diff --git a/src/MapValidator.mts b/src/MapValidator.mts index fe02b26..d89339f 100644 --- a/src/MapValidator.mts +++ b/src/MapValidator.mts @@ -13,52 +13,55 @@ import type { * exceptions. * * All methods (except those found in {@link ObjectValidator}) imply {@link isNotNull}. + * + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values */ -interface MapValidator extends ExtensibleObjectValidator +interface MapValidator extends ExtensibleObjectValidator, Map> { /** * Ensures that value does not contain any entries * * @returns the updated validator */ - isEmpty(): MapValidator; + isEmpty(): MapValidator; /** * Ensures that value contains at least one entry. * * @returns the updated validator */ - isNotEmpty(): MapValidator; + isNotEmpty(): MapValidator; /** * @returns a validator for the Map's keys */ - keys(): ArrayValidator; + keys(): ArrayValidator; /** * @param consumer - a function that accepts an {@link ArrayValidator} for the Map's keys * @returns the updated validator * @throws TypeError if consumer is not set */ - keysConsumer(consumer: (actual: ArrayValidator) => void): MapValidator; + keysConsumer(consumer: (actual: ArrayValidator) => void): MapValidator; /** * @returns a validator for the Map's values */ - values(): ArrayValidator; + values(): ArrayValidator; /** * @param consumer - a function that accepts an {@link ArrayValidator} for the Map's values * @returns the updated validator * @throws TypeError if consumer is not set */ - valuesConsumer(consumer: (actual: ArrayValidator) => void): MapValidator; + valuesConsumer(consumer: (actual: ArrayValidator) => void): MapValidator; /** * @returns validator for the Map's entries (an array of * [key, value] for each element in the Map) */ - entries(): ArrayValidator; + entries(): ArrayValidator<[K, V]>; /** * @param consumer - a function that accepts an {@link ArrayValidator} for the Map's entries (an @@ -66,7 +69,7 @@ interface MapValidator extends ExtensibleObjectValidator * @returns the updated validator * @throws TypeError if consumer is not set */ - entriesConsumer(consumer: (actual: ArrayValidator) => void): MapValidator; + entriesConsumer(consumer: (actual: ArrayValidator<[K, V]>) => void): MapValidator; /** * @returns a validator for the number of entries this Map contains @@ -79,9 +82,27 @@ interface MapValidator extends ExtensibleObjectValidator * @returns the updated validator * @throws TypeError if consumer is not set */ - sizeConsumer(consumer: (actual: NumberValidator) => void): MapValidator; + sizeConsumer(consumer: (actual: NumberValidator) => void): MapValidator; + + /** + * @returns a validator for the Map + * @deprecated returns this + */ + asMap(): MapValidator; + + asMap(): MapValidator; + + /** + * @param consumer - a function that accepts a {@link MapValidator} for the actual value + * @returns the updated validator + * @throws TypeError if consumer is not set. + * If the actual value is not a Map. + */ + asMapConsumer(consumer: (input: MapValidator) => void): MapValidator; + + asMapConsumer(consumer: (input: MapValidator) => void): MapValidator; - getActual(): Map | void; + getActual(): Map | undefined; } export {type MapValidator}; \ No newline at end of file diff --git a/src/MapVerifier.mts b/src/MapVerifier.mts index babe61c..11788d4 100644 --- a/src/MapVerifier.mts +++ b/src/MapVerifier.mts @@ -8,8 +8,11 @@ import type { * Verifies the requirements of a Map. *

* All methods (except those found in {@link ObjectVerifier}) imply {@link isNotNull}. + * + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values */ -interface MapVerifier extends ObjectVerifier +interface MapVerifier extends ObjectVerifier> { /** * Ensures that value does not contain any entries @@ -17,7 +20,7 @@ interface MapVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if the value contains any entries */ - isEmpty(): MapVerifier; + isEmpty(): MapVerifier; /** * Ensures that value contains at least one entry. @@ -25,37 +28,37 @@ interface MapVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if the value does not contain any entries */ - isNotEmpty(): MapVerifier; + isNotEmpty(): MapVerifier; /** * @returns a verifier for the Map's keys */ - keys(): ArrayVerifier; + keys(): ArrayVerifier; /** * @param consumer - a function that accepts an {@link ArrayVerifier} for the Map's keys * @returns the updated verifier * @throws TypeError if consumer is not set */ - keysConsumer(consumer: (actual: ArrayVerifier) => void): MapVerifier; + keysConsumer(consumer: (actual: ArrayVerifier) => void): MapVerifier; /** * @returns a verifier for the Map's values */ - values(): ArrayVerifier; + values(): ArrayVerifier; /** * @param consumer - a function that accepts an {@link ArrayVerifier} for the Map's values * @returns the updated verifier * @throws TypeError if consumer is not set */ - valuesConsumer(consumer: (actual: ArrayVerifier) => void): MapVerifier; + valuesConsumer(consumer: (actual: ArrayVerifier) => void): MapVerifier; /** * @returns a verifier for the Map's entries (an array of [key, value] for * each element in the Map) */ - entries(): ArrayVerifier; + entries(): ArrayVerifier<[K, V]>; /** * @param consumer - a function that accepts an {@link ArrayVerifier} for the Map's entries (an @@ -63,7 +66,7 @@ interface MapVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if consumer is not set */ - entriesConsumer(consumer: (actual: ArrayVerifier) => void): MapVerifier; + entriesConsumer(consumer: (actual: ArrayVerifier<[K, V]>) => void): MapVerifier; /** * @returns a verifier for the number of entries this Map contains @@ -76,9 +79,27 @@ interface MapVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if consumer is not set */ - sizeConsumer(consumer: (actual: NumberVerifier) => void): MapVerifier; + sizeConsumer(consumer: (actual: NumberVerifier) => void): MapVerifier; - getActual(): Map; + /** + * @returns a verifier for the Map + * @deprecated returns this + */ + asMap(): MapVerifier; + + asMap(): MapVerifier; + + /** + * @param consumer - a function that accepts a {@link MapVerifier} for the actual value + * @returns the updated verifier + * @throws TypeError if consumer is not set. + * If the actual value is not a Map. + */ + asMapConsumer(consumer: (input: MapVerifier) => void): MapVerifier; + + asMapConsumer(consumer: (input: MapVerifier) => void): MapVerifier; + + getActual(): Map; } export {type MapVerifier}; \ No newline at end of file diff --git a/src/NumberValidator.mts b/src/NumberValidator.mts index f8a642d..8ad509b 100644 --- a/src/NumberValidator.mts +++ b/src/NumberValidator.mts @@ -3,7 +3,7 @@ import type { ExtensibleObjectValidator } from "./internal/internal.mjs"; -const typedocWorkaround: null | ExtensibleObjectValidator = null; +const typedocWorkaround: null | ExtensibleObjectValidator = null; // noinspection PointlessBooleanExpressionJS if (typedocWorkaround !== null) console.log("WORKAROUND: https://github.com/microsoft/tsdoc/issues/348"); diff --git a/src/ObjectValidator.mts b/src/ObjectValidator.mts index 7cf9d4e..f104850 100644 --- a/src/ObjectValidator.mts +++ b/src/ObjectValidator.mts @@ -7,9 +7,11 @@ import type {ExtensibleObjectValidator} from "./internal/internal.mjs"; * Validators return validation failures through the * {@link ExtensibleObjectValidator.getFailures | getFailures()} method, while Verifiers throw them as * exceptions. + * + * @typeParam T - the type the actual value */ // eslint-disable-next-line @typescript-eslint/no-empty-interface -interface ObjectValidator extends ExtensibleObjectValidator +interface ObjectValidator extends ExtensibleObjectValidator, T> { } diff --git a/src/ObjectVerifier.mts b/src/ObjectVerifier.mts index 4a3f63e..3fde82f 100644 --- a/src/ObjectVerifier.mts +++ b/src/ObjectVerifier.mts @@ -2,9 +2,11 @@ import type {ExtensibleObjectVerifier} from "./internal/internal.mjs"; /** * Verifies the requirements of an object. + * + * @typeParam T - the type the actual value */ // eslint-disable-next-line @typescript-eslint/no-empty-interface -interface ObjectVerifier extends ExtensibleObjectVerifier +interface ObjectVerifier extends ExtensibleObjectVerifier, T> { } diff --git a/src/Requirements.mts b/src/Requirements.mts index 8f59db3..3dfd691 100644 --- a/src/Requirements.mts +++ b/src/Requirements.mts @@ -1,13 +1,34 @@ import type { + ObjectVerifier, + SetVerifier, + ArrayVerifier, + ClassVerifier, + ArrayValidator, + SetValidator, + ClassValidator, ObjectValidator, - ObjectVerifier + MapVerifier, + StringVerifier, + NumberVerifier, + StringValidator, + NumberValidator, + MapValidator } from "./internal/internal.mjs"; import { Configuration, MainGlobalConfiguration, Objects, ObjectValidatorImpl, - ObjectVerifierImpl + ObjectVerifierImpl, + SetValidatorImpl, + SetVerifierImpl, + ArrayVerifierImpl, + ArrayValidatorImpl, + Pluralizer, + ClassVerifierImpl, + ClassValidatorImpl, + MapVerifierImpl, + MapValidatorImpl } from "./internal/internal.mjs"; /** @@ -35,32 +56,79 @@ class Requirements /** * Verifies the requirements of an object. * + * @typeParam T - the type the actual value + * @typeParam E - the type elements in the array or set * @param actual - the actual value * @param name - the name of the value * @returns a verifier * @throws TypeError if name is null * @throws RangeError if name is empty */ - requireThat(actual: unknown, name: string): ObjectVerifier + requireThat(actual: string, name: string): StringVerifier; + requireThat(actual: number, name: string): NumberVerifier; + requireThat(actual: null, name: string): ObjectVerifier; + // eslint-disable-next-line @typescript-eslint/ban-types + requireThat(actual: Function, name: string): ClassVerifier; + requireThat(actual: Array, name: string): ArrayVerifier; + requireThat(actual: Set, name: string): SetVerifier; + requireThat(actual: Map, name: string): MapVerifier; + requireThat(actual: T, name: string): ObjectVerifier; + // eslint-disable-next-line @typescript-eslint/ban-types + requireThat | Set | Map, E, K, V> + (actual: T, name: string): StringVerifier | NumberVerifier | ClassVerifier | ArrayVerifier | SetVerifier | MapVerifier | ObjectVerifier { - return new ObjectVerifierImpl(this.validateThat(actual, name)); + Objects.verifyName(name, "name"); + const typeOfActual = Objects.getTypeInfo(actual); + if (typeOfActual.type === "array") + return new ArrayVerifierImpl(new ArrayValidatorImpl(this.config, actual as E[], name, Pluralizer.ELEMENT, [])); + if (typeOfActual.type === "object" && typeOfActual.name === "Set") + return new SetVerifierImpl(new SetValidatorImpl(this.config, actual as Set, name, [])); + if (typeOfActual.type === "object" && typeOfActual.name === "Map") + return new MapVerifierImpl(new MapValidatorImpl(this.config, actual as Map, name, [])); + if (typeOfActual.type === "class") + { + // eslint-disable-next-line @typescript-eslint/ban-types + return new ClassVerifierImpl(new ClassValidatorImpl(this.config, actual as Function, name, [])); + } + return new ObjectVerifierImpl(new ObjectValidatorImpl(this.config, actual, name, [])); } /** - * Verifies requirements only if assertions are enabled. + * Validates the requirements of an object. * - * @param requirements - the requirements to verify - * @returns the value returned by the operation, or undefined if assertions are disabled + * @typeParam T - the type the actual value + * @typeParam E - the type elements in the array or set + * @param actual - the actual value + * @param name - the name of the value + * @returns validator for the value * @throws TypeError if name is null * @throws RangeError if name is empty - * @see #assertThat */ - assertThatAndReturn(requirements: (requirements: Requirements) => V) + validateThat(actual: string, name: string): StringValidator; + validateThat(actual: number, name: string): NumberValidator; + validateThat(actual: null, name: string): ObjectValidator; + // eslint-disable-next-line @typescript-eslint/ban-types + validateThat(actual: Function, name: string): ClassValidator; + validateThat(actual: Array, name: string): ArrayValidator; + validateThat(actual: Set, name: string): SetValidator; + validateThat(actual: Map, name: string): MapValidator; + validateThat(actual: T, name: string): ObjectValidator; + // eslint-disable-next-line @typescript-eslint/ban-types + validateThat | Set | Map, E, K, V> + (actual: T, name: string): StringValidator | NumberValidator | ClassValidator | ArrayValidator | SetValidator | MapValidator | ObjectValidator { - Objects.requireThatValueIsDefinedAndNotNull(requirements, "requirements"); - if (this.config.assertionsAreEnabled()) - return requirements(this.copy()); - return undefined; + Objects.verifyName(name, "name"); + const typeOfActual = Objects.getTypeInfo(actual); + if (typeOfActual.type === "array") + return new ArrayValidatorImpl(this.config, actual as E[], name, Pluralizer.ELEMENT, []); + if (typeOfActual.type === "object" && typeOfActual.name === "Set") + return new SetValidatorImpl(this.config, actual as Set, name, []); + if (typeOfActual.type === "class") + { + // eslint-disable-next-line @typescript-eslint/ban-types + return new ClassValidatorImpl(this.config, actual as Function, name, []); + } + return new ObjectValidatorImpl(this.config, actual, name, []); } /** @@ -78,28 +146,30 @@ class Requirements } /** - * Returns a copy of this configuration. + * Verifies requirements only if assertions are enabled. * - * @returns a copy of this configuration + * @param requirements - the requirements to verify + * @returns the value returned by the operation, or undefined if assertions are disabled + * @throws TypeError if name is null + * @throws RangeError if name is empty + * @see #assertThat */ - copy() + assertThatAndReturn(requirements: (requirements: Requirements) => V) { - return new Requirements(this.config.copy()); + Objects.requireThatValueIsDefinedAndNotNull(requirements, "requirements"); + if (this.config.assertionsAreEnabled()) + return requirements(this.copy()); + return undefined; } /** - * Validates the requirements of an object. + * Returns a copy of this configuration. * - * @param actual - the actual value - * @param name - the name of the value - * @returns validator for the value - * @throws TypeError if name is null - * @throws RangeError if name is empty + * @returns a copy of this configuration */ - validateThat(actual: unknown, name: string): ObjectValidator + copy() { - Objects.verifyName(name, "name"); - return new ObjectValidatorImpl(this.config, actual, name, []); + return new Requirements(this.config.copy()); } /** diff --git a/src/SetValidator.mts b/src/SetValidator.mts index 0bbb257..20ea004 100644 --- a/src/SetValidator.mts +++ b/src/SetValidator.mts @@ -13,22 +13,24 @@ import type { * exceptions. * * All methods (except those found in {@link ObjectValidator}) imply {@link isNotNull}. + * + * @typeParam E - the type the array elements */ -interface SetValidator extends ExtensibleObjectValidator +interface SetValidator extends ExtensibleObjectValidator, Set> { /** * Ensures that value does not contain any elements. * * @returns the updated validator */ - isEmpty(): SetValidator; + isEmpty(): SetValidator; /** * Ensures that value contains at least one element. * * @returns the updated validator */ - isNotEmpty(): SetValidator; + isNotEmpty(): SetValidator; /** * Ensures that the actual value contains an entry. @@ -39,7 +41,7 @@ interface SetValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - contains(expected: unknown, name?: string): SetValidator; + contains(expected: E, name?: string): SetValidator; /** * Ensures that the actual value contains exactly the same elements as the expected value; nothing less, @@ -52,7 +54,7 @@ interface SetValidator extends ExtensibleObjectValidator * If expected is not an Array or Set. * @throws RangeError if name is empty */ - containsExactly(expected: unknown[] | Set, name?: string): SetValidator; + containsExactly(expected: E[] | Set, name?: string): SetValidator; /** * Ensures that the actual value contains any of the elements in the expected value. @@ -64,7 +66,7 @@ interface SetValidator extends ExtensibleObjectValidator * If expected is not an Array or Set. * @throws RangeError if name is empty */ - containsAny(expected: unknown[] | Set, name?: string): SetValidator; + containsAny(expected: E[] | Set, name?: string): SetValidator; /** * Ensures that the actual value contains all the elements in the expected value. @@ -76,7 +78,7 @@ interface SetValidator extends ExtensibleObjectValidator * If expected is not an Array or Set. * @throws RangeError if name is empty */ - containsAll(expected: unknown[] | Set, name?: string): SetValidator; + containsAll(expected: E[] | Set, name?: string): SetValidator; /** * Ensures that the actual value does not contain an entry. @@ -87,7 +89,7 @@ interface SetValidator extends ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - doesNotContain(entry: unknown, name?: string): SetValidator; + doesNotContain(entry: E, name?: string): SetValidator; /** * Ensures that the actual value does not contain any of the specified elements. @@ -99,7 +101,7 @@ interface SetValidator extends ExtensibleObjectValidator * If elements is not an Array or Set. * @throws RangeError if name is empty */ - doesNotContainAny(elements: unknown[] | Set, name?: string): SetValidator; + doesNotContainAny(elements: E[] | Set, name?: string): SetValidator; /** * Ensures that the array does not contain all the specified elements. @@ -111,7 +113,7 @@ interface SetValidator extends ExtensibleObjectValidator * If elements is not an Array or Set. * @throws RangeError if name is empty */ - doesNotContainAll(elements: unknown[] | Set, name?: string): SetValidator; + doesNotContainAll(elements: E[] | Set, name?: string): SetValidator; /** * @returns a validator for the Set's size @@ -123,21 +125,43 @@ interface SetValidator extends ExtensibleObjectValidator * @returns the updated validator * @throws TypeError if consumer is not set */ - sizeConsumer(consumer: (actual: NumberValidator) => void): SetValidator; + sizeConsumer(consumer: (actual: NumberValidator) => void): SetValidator; /** * @returns a validator for the Set's elements */ - asArray(): ArrayValidator; + asArray(): ArrayValidator; + + asArray(): ArrayValidator; + /** * @param consumer - a function that accepts an {@link ArrayValidator} for the Set's elements * @returns the updated validator * @throws TypeError if consumer is not set */ - asArrayConsumer(consumer: (actual: ArrayValidator) => void): SetValidator; + asArrayConsumer(consumer: (actual: ArrayValidator) => void): SetValidator; + + asArrayConsumer(consumer: (input: ArrayValidator) => void): S; + + /** + * @returns a validator for the Set + * @deprecated returns this + */ + asSet(): SetValidator; + + asSet(): SetValidator; + + /** + * @param consumer - a function that accepts a {@link SetValidator} for the actual value + * @returns the updated validator + * @throws TypeError if consumer is not set + */ + asSetConsumer(consumer: (actual: SetValidator) => void): SetValidator; + + asSetConsumer(consumer: (actual: SetValidator) => void): SetValidator; - getActual(): void | Set; + getActual(): Set | undefined; } export {type SetValidator}; \ No newline at end of file diff --git a/src/SetVerifier.mts b/src/SetVerifier.mts index b45bdcc..1d9a666 100644 --- a/src/SetVerifier.mts +++ b/src/SetVerifier.mts @@ -8,8 +8,10 @@ import type { * Verifies the requirements of a Set. *

* All methods (except those found in {@link ObjectVerifier}) imply {@link isNotNull}. + * + * @typeParam E - the type the array elements */ -interface SetVerifier extends ObjectVerifier +interface SetVerifier extends ObjectVerifier> { /** * Ensures that value does not contain any elements. @@ -17,7 +19,7 @@ interface SetVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if the value contains at least one element */ - isEmpty(): SetVerifier; + isEmpty(): SetVerifier; /** * Ensures that value contains at least one element. @@ -25,7 +27,7 @@ interface SetVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if the value does not contain any elements */ - isNotEmpty(): SetVerifier; + isNotEmpty(): SetVerifier; /** * Ensures that the actual value contains an entry. @@ -37,7 +39,7 @@ interface SetVerifier extends ObjectVerifier * @throws RangeError if name is empty. * If the Set does not contain expected. */ - contains(expected: unknown, name?: string): SetVerifier; + contains(expected: E, name?: string): SetVerifier; /** * Ensures that the actual value contains exactly the same elements as the expected value; nothing less, @@ -52,7 +54,7 @@ interface SetVerifier extends ObjectVerifier * If the actual value is missing any elements in expected. * If the actual value contains elements not found in expected. */ - containsExactly(expected: unknown[] | Set, name?: string): SetVerifier; + containsExactly(expected: E[] | Set, name?: string): SetVerifier; /** * Ensures that the actual value contains any of the elements in the expected value. @@ -66,7 +68,7 @@ interface SetVerifier extends ObjectVerifier * If the actual value is missing any elements in expected. * If the actual value contains elements not found in expected. */ - containsAny(expected: unknown[] | Set, name?: string): SetVerifier; + containsAny(expected: E[] | Set, name?: string): SetVerifier; /** * Ensures that the actual value contains all the elements in the expected value. @@ -79,7 +81,7 @@ interface SetVerifier extends ObjectVerifier * @throws RangeError if name is empty. * If the actual value does not contain all of expected. */ - containsAll(expected: unknown[] | Set, name?: string): SetVerifier; + containsAll(expected: E[] | Set, name?: string): SetVerifier; /** * Ensures that the actual value does not contain an entry. @@ -91,7 +93,7 @@ interface SetVerifier extends ObjectVerifier * @throws RangeError if name is empty. * If the actual value contains entry. */ - doesNotContain(entry: unknown, name?: string): SetVerifier; + doesNotContain(entry: E, name?: string): SetVerifier; /** * Ensures that the actual value does not contain any of the specified elements. @@ -104,7 +106,7 @@ interface SetVerifier extends ObjectVerifier * @throws RangeError if name is empty. * If the array contains any of elements. */ - doesNotContainAny(elements: unknown[] | Set, name?: string): SetVerifier; + doesNotContainAny(elements: E[] | Set, name?: string): SetVerifier; /** * Ensures that the array does not contain all the specified elements. @@ -117,7 +119,7 @@ interface SetVerifier extends ObjectVerifier * @throws RangeError if name is empty. * If the actual value contains all of elements. */ - doesNotContainAll(elements: unknown[] | Set, name?: string): SetVerifier; + doesNotContainAll(elements: E[] | Set, name?: string): SetVerifier; /** * @returns a verifier for the Set's size @@ -129,21 +131,42 @@ interface SetVerifier extends ObjectVerifier * @returns the updated verifier * @throws TypeError if consumer is not set */ - sizeConsumer(consumer: (actual: NumberVerifier) => void): SetVerifier; + sizeConsumer(consumer: (actual: NumberVerifier) => void): SetVerifier; /** * @returns a verifier for the Set's elements */ - asArray(): ArrayVerifier; + asArray(): ArrayVerifier; + + asArray(): ArrayVerifier; /** * @param consumer - a function that accepts an {@link ArrayVerifier} for the Set's elements * @returns the updated verifier * @throws TypeError if consumer is not set */ - asArrayConsumer(consumer: (actual: ArrayVerifier) => void): SetVerifier; + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): S; + + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): SetVerifier; + + /** + * @returns a verifier for the Set + * @deprecated returns this + */ + asSet(): SetVerifier; + + asSet(): SetVerifier; + + /** + * @param consumer - a function that accepts a {@link SetVerifier} for the actual value + * @returns the updated verifier + * @throws TypeError if consumer is not set + */ + asSetConsumer(consumer: (actual: SetVerifier) => void): SetVerifier; + + asSetConsumer(consumer: (actual: SetVerifier) => void): SetVerifier; - getActual(): Set; + getActual(): Set; } export {type SetVerifier}; \ No newline at end of file diff --git a/src/StringValidator.mts b/src/StringValidator.mts index 3718220..26934ff 100644 --- a/src/StringValidator.mts +++ b/src/StringValidator.mts @@ -14,7 +14,7 @@ import type { * All methods (except for {@link asString} and those found in {@link ObjectValidator}) imply * {@link isNotNull}. */ -interface StringValidator extends ExtensibleObjectValidator +interface StringValidator extends ExtensibleObjectValidator { /** * Ensures that the actual value starts with a value. @@ -127,7 +127,7 @@ interface StringValidator extends ExtensibleObjectValidator */ asStringConsumer(consumer: (actual: StringValidator) => void): StringValidator; - getActual(): string | void; + getActual(): string | undefined; } export {type StringValidator}; \ No newline at end of file diff --git a/src/StringVerifier.mts b/src/StringVerifier.mts index acb7a12..31d9510 100644 --- a/src/StringVerifier.mts +++ b/src/StringVerifier.mts @@ -4,7 +4,7 @@ import type { ObjectVerifier } from "./internal/internal.mjs"; -const typedocWorkaround: null | ExtensibleObjectValidator = null; +const typedocWorkaround: null | ExtensibleObjectValidator = null; // noinspection PointlessBooleanExpressionJS if (typedocWorkaround !== null) console.log("WORKAROUND: https://github.com/microsoft/tsdoc/issues/348"); @@ -20,7 +20,7 @@ if (typedocWorkaround !== null) * All methods (except for {@link asString} and those found in {@link ObjectValidator}) imply * {@link isNotNull}. */ -interface StringVerifier extends ObjectVerifier +interface StringVerifier extends ObjectVerifier { /** * Ensures that the actual value starts with a value. diff --git a/src/extension/ExtensibleNumberValidator.mts b/src/extension/ExtensibleNumberValidator.mts index e444d6e..3f6283a 100644 --- a/src/extension/ExtensibleNumberValidator.mts +++ b/src/extension/ExtensibleNumberValidator.mts @@ -7,7 +7,7 @@ import type {ExtensibleObjectValidator} from "../internal/internal.mjs"; * * @typeParam S - the type of validator returned by the methods */ -interface ExtensibleNumberValidator extends ExtensibleObjectValidator +interface ExtensibleNumberValidator extends ExtensibleObjectValidator { /** * Ensures that the actual value is negative. @@ -143,7 +143,7 @@ interface ExtensibleNumberValidator extends ExtensibleObjectValidator */ isNotFinite(): S; - getActual(): number | void; + getActual(): number | undefined; } export {type ExtensibleNumberValidator}; \ No newline at end of file diff --git a/src/extension/ExtensibleNumberVerifier.mts b/src/extension/ExtensibleNumberVerifier.mts index 31a056c..1673078 100644 --- a/src/extension/ExtensibleNumberVerifier.mts +++ b/src/extension/ExtensibleNumberVerifier.mts @@ -7,7 +7,7 @@ import type {ExtensibleObjectVerifier} from "../internal/internal.mjs"; * * @typeParam S - the type of validator returned by the methods */ -interface ExtensibleNumberVerifier extends ExtensibleObjectVerifier +interface ExtensibleNumberVerifier extends ExtensibleObjectVerifier { /** * Ensures that the actual value is negative. diff --git a/src/extension/ExtensibleObjectValidator.mts b/src/extension/ExtensibleObjectValidator.mts index 858247e..bbed5b3 100644 --- a/src/extension/ExtensibleObjectValidator.mts +++ b/src/extension/ExtensibleObjectValidator.mts @@ -1,21 +1,22 @@ import type { - ArrayValidator, + StringValidator, + ValidationFailure, BooleanValidator, - ClassValidator, - InetAddressValidator, - MapValidator, NumberValidator, + InetAddressValidator, + ClassValidator, + ArrayValidator, SetValidator, - StringValidator, - ValidationFailure + MapValidator } from "../internal/internal.mjs"; /** * Validates the requirements of an object. * * @typeParam S - the type of validator returned by the methods + * @typeParam T - the type the actual value */ -interface ExtensibleObjectValidator +interface ExtensibleObjectValidator { /** * Ensures that the actual value is equal to a value. @@ -26,7 +27,7 @@ interface ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - isEqualTo(expected: unknown, name?: string): S; + isEqualTo(expected: T, name?: string): S; /** * Ensures that the actual value is not equal to a value. @@ -37,7 +38,7 @@ interface ExtensibleObjectValidator * @throws TypeError if name is null * @throws RangeError if name is empty */ - isNotEqualTo(value: unknown, name?: string): S; + isNotEqualTo(value: T, name?: string): S; /** * Ensures that the actual value is a primitive. To check if the actual value is an object, use @@ -119,9 +120,9 @@ interface ExtensibleObjectValidator /** * Returns the actual value. * - * @returns the actual value + * @returns undefined if the validation failed */ - getActual(): unknown; + getActual(): T | undefined; /** * @returns a validator for the object's string representation @@ -134,19 +135,7 @@ interface ExtensibleObjectValidator * @returns the updated validator * @throws TypeError if consumer is not set */ - asStringConsumer(consumer: (actual: unknown) => StringValidator): S; - - /** - * @returns a validator for the Array - */ - asArray(): ArrayValidator; - - /** - * @param consumer - a function that accepts a {@link ArrayValidator} for the actual value - * @returns the updated validator - * @throws TypeError if consumer is not set - */ - asArrayConsumer(consumer: (input: ArrayValidator) => void): S; + asStringConsumer(consumer: (actual: StringValidator) => StringValidator): S; /** * @returns a validator for the boolean @@ -173,29 +162,49 @@ interface ExtensibleObjectValidator asNumberConsumer(consumer: (input: NumberValidator) => void): S; /** + * @typeParam E - the type the array elements + * @returns a validator for the Array + */ + asArray(): ArrayValidator; + + /** + * @typeParam E - the type the array elements + * @param consumer - a function that accepts a {@link ArrayValidator} for the actual value + * @returns the updated validator + * @throws TypeError if consumer is not set + */ + asArrayConsumer(consumer: (input: ArrayValidator) => void): S; + + /** + * @typeParam E - the type the set elements * @returns a validator for the Set */ - asSet(): SetValidator; + asSet(): SetValidator; /** + * @typeParam E - the type the array elements * @param consumer - a function that accepts a {@link SetValidator} for the actual value * @returns the updated validator * @throws TypeError if consumer is not set */ - asSetConsumer(consumer: (actual: SetValidator) => void): S; + asSetConsumer(consumer: (actual: SetValidator) => void): S; /** + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values * @returns a validator for the Map */ - asMap(): MapValidator; + asMap(): MapValidator; /** + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values * @param consumer - a function that accepts a {@link MapValidator} for the actual value * @returns the updated validator * @throws TypeError if consumer is not set. * If the actual value is not a Map. */ - asMapConsumer(consumer: (input: MapValidator) => void): S; + asMapConsumer(consumer: (input: MapValidator) => void): S; /** * @returns a validator for the value's Internet address representation diff --git a/src/extension/ExtensibleObjectVerifier.mts b/src/extension/ExtensibleObjectVerifier.mts index e181a5c..75e4958 100644 --- a/src/extension/ExtensibleObjectVerifier.mts +++ b/src/extension/ExtensibleObjectVerifier.mts @@ -1,20 +1,21 @@ import type { - ArrayVerifier, + StringVerifier, BooleanVerifier, - ClassVerifier, - InetAddressVerifier, - MapVerifier, NumberVerifier, + InetAddressVerifier, + ClassVerifier, + ArrayVerifier, SetVerifier, - StringVerifier + MapVerifier } from "../internal/internal.mjs"; /** * Verifies the requirements of an object. * * @typeParam S - the type of verifier returned by the methods + * @typeParam T - the type the actual value */ -interface ExtensibleObjectVerifier +interface ExtensibleObjectVerifier { /** * Ensures that the actual value is equal to a value. @@ -26,7 +27,7 @@ interface ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the actual value is not equal to expected. */ - isEqualTo(expected: unknown, name?: string): S; + isEqualTo(expected: T, name?: string): S; /** * Throws an exception if the validation failed. @@ -49,7 +50,7 @@ interface ExtensibleObjectVerifier * @throws RangeError if name is empty. * If the actual value is equal to value. */ - isNotEqualTo(value: unknown, name?: string): S; + isNotEqualTo(value: T, name?: string): S; /** * Ensures that the actual value is a primitive. To check if the actual value is an object, use @@ -138,7 +139,7 @@ interface ExtensibleObjectVerifier * * @returns the actual value */ - getActual(): unknown; + getActual(): T; /** * @returns a verifier for the object's string representation @@ -153,20 +154,6 @@ interface ExtensibleObjectVerifier */ asStringConsumer(consumer: (actual: StringVerifier) => void): S; - /** - * @returns a verifier for the Array - * @throws TypeError if the actual value is not an Array - */ - asArray(): ArrayVerifier; - - /** - * @param consumer - a function that accepts a {@link ArrayVerifier} for the actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set. - * If the actual value is not an Array. - */ - asArrayConsumer(consumer: (actual: ArrayVerifier) => void): S; - /** * @returns a verifier for the boolean * @throws TypeError if the actual value is not a boolean @@ -196,32 +183,54 @@ interface ExtensibleObjectVerifier asNumberConsumer(consumer: (actual: NumberVerifier) => void): S; /** + * @typeParam E - the type the array elements + * @returns a verifier for the Array + * @throws TypeError if the actual value is not an Array + */ + asArray(): ArrayVerifier; + + /** + * @typeParam E - the type the array elements + * @param consumer - a function that accepts a {@link ArrayVerifier} for the actual value + * @returns the updated verifier + * @throws TypeError if consumer is not set. + * If the actual value is not an Array. + */ + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): S; + + /** + * @typeParam E - the type the set elements * @returns a verifier for the Set * @throws TypeError if the actual value is not a Set */ - asSet(): SetVerifier; + asSet(): SetVerifier; /** + * @typeParam E - the type the set elements * @param consumer - a function that accepts a {@link SetVerifier} for the actual value * @returns the updated verifier * @throws TypeError if consumer is not set. * If the actual value is not a Set. */ - asSetConsumer(consumer: (actual: SetVerifier) => void): S; + asSetConsumer(consumer: (actual: SetVerifier) => void): S; /** + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values * @returns a verifier for the Map * @throws TypeError if the actual value is not a Map */ - asMap(): MapVerifier; + asMap(): MapVerifier; /** + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values * @param consumer - a function that accepts a {@link MapVerifier} for the actual value * @returns the updated verifier * @throws TypeError if consumer is not set. * If the actual value is not a Map. */ - asMapConsumer(consumer: (actual: MapVerifier) => void): S; + asMapConsumer(consumer: (actual: MapVerifier) => void): S; /** * @returns a verifier for the value's Internet address representation diff --git a/src/index.mts b/src/index.mts index d36107b..57ab880 100644 --- a/src/index.mts +++ b/src/index.mts @@ -5,5 +5,9 @@ export assertThatAndReturn, validateThat } from "./DefaultRequirements.mjs"; -export {Requirements} from "./internal/internal.mjs"; -export {GlobalRequirements} from "./internal/internal.mjs"; \ No newline at end of file +export { + Requirements, + GlobalRequirements, + Configuration, + TerminalEncoding +} from "./internal/internal.mjs"; \ No newline at end of file diff --git a/src/internal/ArrayValidatorImpl.mts b/src/internal/ArrayValidatorImpl.mts index 994d2e0..d1efa0b 100644 --- a/src/internal/ArrayValidatorImpl.mts +++ b/src/internal/ArrayValidatorImpl.mts @@ -16,11 +16,13 @@ import { /** * Default implementation of ArrayValidator. + * + * @typeParam T - the type the actual value */ -class ArrayValidatorImpl extends AbstractObjectValidator - implements ArrayValidator +class ArrayValidatorImpl extends AbstractObjectValidator, E[]> + implements ArrayValidator { - private readonly actualArray: void | unknown[]; + private readonly actualArray: E[] | undefined; private readonly pluralizer: Pluralizer; /** @@ -34,7 +36,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: void | unknown[], name: string, pluralizer: Pluralizer, + constructor(configuration: Configuration, actual: E[] | undefined, name: string, pluralizer: Pluralizer, failures: ValidationFailure[]) { super(configuration, actual, name, failures); @@ -52,9 +54,8 @@ class ArrayValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid = this.actualArray as unknown[]; - if (actualAsNotVoid.length > 0) + const actualAsDefined = this.actualArray as E[]; + if (actualAsDefined.length > 0) { const failure = new ValidationFailure(this.config, RangeError, this.name + " must be empty"). addContext("Actual", this.actualArray); @@ -68,9 +69,8 @@ class ArrayValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid = this.actualArray as unknown[]; - if (actualAsNotVoid.length === 0) + const actualAsDefined = this.actualArray as E[]; + if (actualAsDefined.length === 0) { const failure = new ValidationFailure(this.config, RangeError, this.name + " may not be empty."); this.failures.push(failure); @@ -85,7 +85,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator * @param element - an element * @returns true if arrays contains the element */ - private static arrayContainsElement(array: unknown[], element: unknown): boolean + private arrayContainsElement(array: E[], element: E): boolean { // indexOf(), includes() do not work for multidimensional arrays: http://stackoverflow.com/a/24943461/14731 for (let i = 0; i < array.length; ++i) @@ -101,11 +101,11 @@ class ArrayValidatorImpl extends AbstractObjectValidator * @param expected - an array of expected values * @returns true if actual contains any of the expected elements */ - private static arrayContainsAny(array: unknown[], expected: unknown[]): boolean + private arrayContainsAny(array: E[], expected: E[]): boolean { for (const element of expected) { - if (ArrayValidatorImpl.arrayContainsElement(array, element)) + if (this.arrayContainsElement(array, element)) return true; } return false; @@ -118,26 +118,25 @@ class ArrayValidatorImpl extends AbstractObjectValidator * @param expected - an array of expected elements * @returns true if actual contains all the expected elements */ - private static arrayContainsAll(array: unknown[], expected: unknown[]): boolean + private arrayContainsAll(array: E[], expected: E[]): boolean { for (const element of expected) { - if (!ArrayValidatorImpl.arrayContainsElement(array, element)) + if (!this.arrayContainsElement(array, element)) return false; } return true; } - contains(element: unknown, name?: string): ArrayValidator + contains(element: E, name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid = this.actualArray as unknown[]; - if (!ArrayValidatorImpl.arrayContainsElement(actualAsNotVoid, element)) + const actualAsDefined = this.actualArray as E[]; + if (!this.arrayContainsElement(actualAsDefined, element)) { let failure; if (name) @@ -158,7 +157,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - containsExactly(expected: unknown[], name?: string): ArrayValidator + containsExactly(expected: E[], name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); @@ -166,10 +165,9 @@ class ArrayValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid = this.actualArray as unknown[]; + const actualAsDefined = this.actualArray as E[]; const expectedAsSet = new Set(expected); - const actualAsSet = new Set(actualAsNotVoid); + const actualAsSet = new Set(actualAsDefined); const missing = new Set([...expectedAsSet].filter(x => !actualAsSet.has(x))); const unwanted = new Set([...actualAsSet].filter(x => !expectedAsSet.has(x))); if (missing.size !== 0 || unwanted.size !== 0) @@ -197,7 +195,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - containsAny(expected: unknown[], name?: string): ArrayValidator + containsAny(expected: E[], name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); @@ -206,8 +204,8 @@ class ArrayValidatorImpl extends AbstractObjectValidator Objects.requireThatTypeOf(expected, "expected", "array"); - const actualAsNotVoid = this.actualArray as unknown[]; - if (!ArrayValidatorImpl.arrayContainsAny(actualAsNotVoid, expected)) + const actualAsDefined = this.actualArray as E[]; + if (!this.arrayContainsAny(actualAsDefined, expected)) { let failure; if (name) @@ -228,7 +226,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - containsAll(expected: unknown[], name?: string): ArrayValidator + containsAll(expected: E[], name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); @@ -237,11 +235,11 @@ class ArrayValidatorImpl extends AbstractObjectValidator Objects.requireThatTypeOf(expected, "expected", "array"); - const actualAsNotVoid = this.actualArray as unknown[]; - if (!ArrayValidatorImpl.arrayContainsAll(actualAsNotVoid, expected)) + const actualAsDefined = this.actualArray as E[]; + if (!this.arrayContainsAll(actualAsDefined, expected)) { const expectedAsSet = new Set(expected); - const actualAsSet = new Set(actualAsNotVoid); + const actualAsSet = new Set(actualAsDefined); const missing = new Set([...expectedAsSet].filter(x => !actualAsSet.has(x))); let failure; if (name) @@ -264,16 +262,15 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - doesNotContain(element: unknown, name?: string): ArrayValidator + doesNotContain(element: E, name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid = this.actualArray as unknown[]; - if (ArrayValidatorImpl.arrayContainsElement(actualAsNotVoid, element)) + const actualAsDefined = this.actualArray as E[]; + if (this.arrayContainsElement(actualAsDefined, element)) { let failure; if (name) @@ -294,7 +291,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - doesNotContainAny(elements: unknown[], name?: string): ArrayValidator + doesNotContainAny(elements: E[], name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); @@ -303,8 +300,8 @@ class ArrayValidatorImpl extends AbstractObjectValidator Objects.requireThatTypeOf(elements, "elements", "array"); - const actualAsNotVoid = this.actualArray as unknown[]; - if (ArrayValidatorImpl.arrayContainsAny(actualAsNotVoid, elements)) + const actualAsDefined = this.actualArray as E[]; + if (this.arrayContainsAny(actualAsDefined, elements)) { let failure; if (name) @@ -325,7 +322,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - doesNotContainAll(elements: unknown[], name?: string): ArrayValidator + doesNotContainAll(elements: E[], name?: string): ArrayValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); @@ -334,11 +331,11 @@ class ArrayValidatorImpl extends AbstractObjectValidator Objects.requireThatTypeOf(elements, "elements", "array"); - const actualAsNotVoid = this.actualArray as unknown[]; - if (ArrayValidatorImpl.arrayContainsAll(actualAsNotVoid, elements)) + const actualAsDefined = this.actualArray as E[]; + if (this.arrayContainsAll(actualAsDefined, elements)) { const elementsAsSet = new Set(elements); - const actualAsSet = new Set(actualAsNotVoid); + const actualAsSet = new Set(actualAsDefined); const missing = new Set([...elementsAsSet].filter(x => !actualAsSet.has(x))); let failure; if (name) @@ -361,16 +358,15 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - doesNotContainDuplicates(): ArrayValidator + doesNotContainDuplicates(): ArrayValidator { if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - const unique = new Set(); const duplicates = new Set(); - const actualAsNotVoid = this.actualArray as unknown[]; - for (const element of actualAsNotVoid) + const actualAsDefined = this.actualArray as E[]; + for (const element of actualAsDefined) { if (unique.has(element)) duplicates.add(element); @@ -390,7 +386,7 @@ class ArrayValidatorImpl extends AbstractObjectValidator length(): NumberValidator { - let value: void | unknown[]; + let value: E[] | undefined; let length; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) { @@ -399,14 +395,14 @@ class ArrayValidatorImpl extends AbstractObjectValidator } else { - value = this.actualArray as unknown[]; + value = this.actualArray as E[]; length = value.length; } return new SizeValidatorImpl(this.config, value, this.name, length, this.name + ".length", this.pluralizer, this.failures); } - lengthConsumer(consumer: (length: NumberValidator) => void): ArrayValidator + lengthConsumer(consumer: (length: NumberValidator) => void): ArrayValidator { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -414,20 +410,42 @@ class ArrayValidatorImpl extends AbstractObjectValidator return this; } - asSet(): SetValidator + asArray(): ArrayValidator; + asArray(): ArrayValidator + { + return this; + } + + asArrayConsumer(consumer: (input: ArrayValidator) => void): ArrayValidator + asArrayConsumer(consumer: (input: ArrayValidator) => void): ArrayValidator { - let value: void | Set; + return super.asArrayConsumer(consumer); + } + + asSet(): SetValidator; + asSet(): SetValidator + { + let value: Set | undefined; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else { - const actualAsNotVoid = this.actualArray as unknown[]; - value = new Set(actualAsNotVoid); + const actualAsDefined = this.actualArray as E[]; + value = new Set(actualAsDefined); } - return new SetValidatorImpl(this.config, value, this.name + ".asSet()", this.failures); + return new SetValidatorImpl(this.config, value, this.name + ".asSet()", this.failures); + } + + asSetConsumer(consumer: (actual: SetValidator) => void): S; + asSetConsumer(consumer: (actual: SetValidator) => void): ArrayValidator + { + Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); + if (this.failures.length === 0) + consumer(this.asSet()); + return this.getThis(); } - getActual(): void | unknown[] + getActual(): E[] | undefined { return this.actualArray; } diff --git a/src/internal/ArrayVerifierImpl.mts b/src/internal/ArrayVerifierImpl.mts index 2883b2a..0e4532b 100644 --- a/src/internal/ArrayVerifierImpl.mts +++ b/src/internal/ArrayVerifierImpl.mts @@ -13,9 +13,11 @@ import { /** * Default implementation of ArrayVerifier. + * + * @typeParam E - the type the array elements */ -class ArrayVerifierImpl extends AbstractObjectVerifier - implements ArrayVerifier +class ArrayVerifierImpl extends AbstractObjectVerifier, ArrayValidator, E[]> + implements ArrayVerifier { /** * Creates a new ArrayVerifierImpl. @@ -23,7 +25,7 @@ class ArrayVerifierImpl extends AbstractObjectVerifiervalidator is null or undefined */ - constructor(validator: ArrayValidator) + constructor(validator: ArrayValidator) { super(validator); } @@ -45,49 +47,49 @@ class ArrayVerifierImpl extends AbstractObjectVerifier this.getThis()); } - contains(element: unknown, name?: string) + contains(element: E, name?: string) { this.validator.contains(element, name); return this.validationResult(() => this.getThis()); } - containsExactly(expected: unknown[], name?: string) + containsExactly(expected: E[], name?: string) { this.validator.containsExactly(expected, name); return this.validationResult(() => this.getThis()); } - containsAny(expected: unknown[], name?: string) + containsAny(expected: E[], name?: string) { this.validator.containsAny(expected, name); return this.validationResult(() => this.getThis()); } - containsAll(expected: unknown[], name?: string) + containsAll(expected: E[], name?: string) { this.validator.containsAll(expected, name); return this.validationResult(() => this.getThis()); } - doesNotContain(element: unknown, name?: string) + doesNotContain(element: E, name?: string) { this.validator.doesNotContain(element, name); return this.validationResult(() => this.getThis()); } - doesNotContainAny(elements: unknown[], name?: string): ArrayVerifier + doesNotContainAny(elements: E[], name?: string): ArrayVerifier { this.validator.doesNotContainAny(elements, name); return this.validationResult(() => this.getThis()); } - doesNotContainAll(elements: unknown[], name?: string): ArrayVerifier + doesNotContainAll(elements: E[], name?: string): ArrayVerifier { this.validator.doesNotContainAll(elements, name); return this.validationResult(() => this.getThis()); } - doesNotContainDuplicates(): ArrayVerifier + doesNotContainDuplicates(): ArrayVerifier { this.validator.doesNotContainDuplicates(); return this.validationResult(() => this.getThis()); @@ -99,30 +101,27 @@ class ArrayVerifierImpl extends AbstractObjectVerifier new NumberVerifierImpl(newValidator)) as NumberVerifier; } - lengthConsumer(consumer: (actual: NumberVerifier) => void): ArrayVerifier + lengthConsumer(consumer: (actual: NumberVerifier) => void): ArrayVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.length()); return this; } - asSet(): SetVerifier + asSet(): SetVerifier; + asSet(): SetVerifier { const newValidator = this.validator.asSet(); - return this.validationResult(() => new SetVerifierImpl(newValidator)) as SetVerifier; + return this.validationResult(() => new SetVerifierImpl(newValidator)) as SetVerifier; } - asSetConsumer(consumer: (actual: SetVerifier) => void): ArrayVerifier + asSetConsumer(consumer: (actual: SetVerifier) => void): S; + asSetConsumer(consumer: (actual: SetVerifier) => void): ArrayVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asSet()); return this; } - - getActual(): unknown[] - { - return super.getActual() as unknown[]; - } } export {ArrayVerifierImpl}; \ No newline at end of file diff --git a/src/internal/BooleanValidatorImpl.mts b/src/internal/BooleanValidatorImpl.mts index bcbaae4..bb69832 100644 --- a/src/internal/BooleanValidatorImpl.mts +++ b/src/internal/BooleanValidatorImpl.mts @@ -10,7 +10,7 @@ import { /** * Default implementation of BooleanValidator. */ -class BooleanValidatorImpl extends AbstractObjectValidator +class BooleanValidatorImpl extends AbstractObjectValidator implements BooleanValidator { private readonly actualBoolean: boolean; @@ -25,7 +25,7 @@ class BooleanValidatorImpl extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: unknown, name: string, failures: ValidationFailure[]) + constructor(configuration: Configuration, actual: boolean | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); this.actualBoolean = actual as boolean; @@ -41,7 +41,6 @@ class BooleanValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - if (!this.actualBoolean) { const failure = new ValidationFailure(this.config, RangeError, this.name + " must be true."). @@ -65,9 +64,9 @@ class BooleanValidatorImpl extends AbstractObjectValidator return this; } - getActual(): void | boolean + getActual(): boolean | undefined { - return super.getActual() as void | boolean; + return super.getActual() as boolean | undefined; } } diff --git a/src/internal/BooleanVerifierImpl.mts b/src/internal/BooleanVerifierImpl.mts index bfb0d25..b844756 100644 --- a/src/internal/BooleanVerifierImpl.mts +++ b/src/internal/BooleanVerifierImpl.mts @@ -7,7 +7,7 @@ import {AbstractObjectVerifier} from "./internal.mjs"; /** * Default implementation of BooleanVerifier. */ -class BooleanVerifierImpl extends AbstractObjectVerifier +class BooleanVerifierImpl extends AbstractObjectVerifier implements BooleanVerifier { /** diff --git a/src/internal/ClassValidatorImpl.mts b/src/internal/ClassValidatorImpl.mts index fc497cc..0d763e5 100644 --- a/src/internal/ClassValidatorImpl.mts +++ b/src/internal/ClassValidatorImpl.mts @@ -11,7 +11,8 @@ import { /** * Default implementation of ClassValidator. */ -class ClassValidatorImpl extends AbstractObjectValidator +// eslint-disable-next-line @typescript-eslint/ban-types +class ClassValidatorImpl extends AbstractObjectValidator implements ClassValidator { protected getThis() @@ -33,7 +34,8 @@ class ClassValidatorImpl extends AbstractObjectValidator * isIpV6, isHostname are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: unknown, name: string, failures: ValidationFailure[]) + // eslint-disable-next-line @typescript-eslint/ban-types + constructor(configuration: Configuration, actual: Function | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); @@ -47,7 +49,6 @@ class ClassValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - const typeInfo = Objects.getTypeInfo(type); let failure; switch (typeInfo.type) @@ -107,7 +108,7 @@ class ClassValidatorImpl extends AbstractObjectValidator } // eslint-disable-next-line @typescript-eslint/ban-types - getActual(): void | Function + getActual(): Function | undefined { return this.actualClass; } diff --git a/src/internal/ClassVerifierImpl.mts b/src/internal/ClassVerifierImpl.mts index 2f84d39..06e21bb 100644 --- a/src/internal/ClassVerifierImpl.mts +++ b/src/internal/ClassVerifierImpl.mts @@ -7,7 +7,8 @@ import {AbstractObjectVerifier} from "./internal.mjs"; /** * Default implementation of ClassVerifier. */ -class ClassVerifierImpl extends AbstractObjectVerifier +// eslint-disable-next-line @typescript-eslint/ban-types +class ClassVerifierImpl extends AbstractObjectVerifier implements ClassVerifier { /** @@ -39,13 +40,6 @@ class ClassVerifierImpl extends AbstractObjectVerifier this.getThis()); } - - // eslint-disable-next-line @typescript-eslint/ban-types - getActual() - { - // eslint-disable-next-line @typescript-eslint/ban-types - return super.getActual() as Function; - } } export {ClassVerifierImpl}; \ No newline at end of file diff --git a/src/internal/InetAddressValidatorImpl.mts b/src/internal/InetAddressValidatorImpl.mts index 5d2ae9d..5c76c6f 100644 --- a/src/internal/InetAddressValidatorImpl.mts +++ b/src/internal/InetAddressValidatorImpl.mts @@ -11,7 +11,7 @@ import { /** * Default implementation of InetAddressValidator. */ -class InetAddressValidatorImpl extends AbstractObjectValidator +class InetAddressValidatorImpl extends AbstractObjectValidator implements InetAddressValidator { private readonly addressIsIpV4: boolean; @@ -32,7 +32,7 @@ class InetAddressValidatorImpl extends AbstractObjectValidatorisIpV6, isHostname are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: void | string, name: string, isIpV4: boolean, isIpV6: boolean, + constructor(configuration: Configuration, actual: string | undefined, name: string, isIpV4: boolean, isIpV6: boolean, isHostname: boolean, failures: ValidationFailure[]) { super(configuration, actual, name, failures); @@ -55,7 +55,6 @@ class InetAddressValidatorImpl extends AbstractObjectValidator 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - if (!this.addressIsIpV4) { const failure = new ValidationFailure(this.config, RangeError, @@ -71,7 +70,6 @@ class InetAddressValidatorImpl extends AbstractObjectValidator 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - if (!this.addressIsIpV6) { const failure = new ValidationFailure(this.config, RangeError, diff --git a/src/internal/InetAddressVerifierImpl.mts b/src/internal/InetAddressVerifierImpl.mts index b9e1b5f..0ffde9e 100644 --- a/src/internal/InetAddressVerifierImpl.mts +++ b/src/internal/InetAddressVerifierImpl.mts @@ -7,7 +7,7 @@ import {AbstractObjectVerifier} from "./internal.mjs"; /** * Default implementation of InetAddressVerifier. */ -class InetAddressVerifierImpl extends AbstractObjectVerifier +class InetAddressVerifierImpl extends AbstractObjectVerifier implements InetAddressVerifier { /** @@ -43,11 +43,6 @@ class InetAddressVerifierImpl extends AbstractObjectVerifier this.getThis()); } - - getActual(): string - { - return super.getActual() as string; - } } export {InetAddressVerifierImpl}; \ No newline at end of file diff --git a/src/internal/MapValidatorImpl.mts b/src/internal/MapValidatorImpl.mts index 1f47396..5e1bdb4 100644 --- a/src/internal/MapValidatorImpl.mts +++ b/src/internal/MapValidatorImpl.mts @@ -15,11 +15,14 @@ import { /** * Default implementation of MapValidator. + * + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values */ -class MapValidatorImpl extends AbstractObjectValidator - implements MapValidator +class MapValidatorImpl extends AbstractObjectValidator, Map> + implements MapValidator { - private readonly actualMap: Map; + private readonly actualMap: Map; /** * Creates a new MapValidatorImpl. @@ -31,10 +34,10 @@ class MapValidatorImpl extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: unknown, name: string, failures: ValidationFailure[]) + constructor(configuration: Configuration, actual: Map | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); - this.actualMap = this.actual as Map; + this.actualMap = this.actual as Map; } protected getThis() @@ -70,7 +73,7 @@ class MapValidatorImpl extends AbstractObjectValidator keys() { - let value: void | unknown[]; + let value: K[] | undefined; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else @@ -78,7 +81,7 @@ class MapValidatorImpl extends AbstractObjectValidator return new ArrayValidatorImpl(this.config, value, this.name + ".keys()", Pluralizer.KEY, this.failures); } - keysConsumer(consumer: (actual: ArrayValidator) => void) + keysConsumer(consumer: (actual: ArrayValidator) => void) { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -88,7 +91,7 @@ class MapValidatorImpl extends AbstractObjectValidator values() { - let value: void | unknown[]; + let value: V[] | undefined; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else @@ -97,7 +100,7 @@ class MapValidatorImpl extends AbstractObjectValidator this.failures); } - valuesConsumer(consumer: (actual: ArrayValidator) => void) + valuesConsumer(consumer: (actual: ArrayValidator) => void) { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -107,7 +110,7 @@ class MapValidatorImpl extends AbstractObjectValidator entries() { - let value: void | unknown[]; + let value: [K, V][] | undefined; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else @@ -116,7 +119,7 @@ class MapValidatorImpl extends AbstractObjectValidator this.failures); } - entriesConsumer(consumer: (actual: ArrayValidator) => void): MapValidator + entriesConsumer(consumer: (actual: ArrayValidator<[K, V]>) => void): MapValidator { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -126,7 +129,7 @@ class MapValidatorImpl extends AbstractObjectValidator size(): NumberValidator { - let value: void | unknown[] | Set | Map | string; + let value: unknown; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else @@ -135,7 +138,7 @@ class MapValidatorImpl extends AbstractObjectValidator Pluralizer.ENTRY, this.failures); } - sizeConsumer(consumer: (actual: NumberValidator) => void): MapValidator + sizeConsumer(consumer: (actual: NumberValidator) => void): MapValidator { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -143,7 +146,20 @@ class MapValidatorImpl extends AbstractObjectValidator return this; } - getActual(): Map + asMap(): MapValidator; + asMap(): MapValidator + { + return this; + } + + asMapConsumer(consumer: (input: MapValidator) => void): MapValidator; + asMapConsumer(consumer: (input: MapValidator) => void): MapValidator + { + return super.asMapConsumer(consumer); + } + + + getActual(): Map { return this.actualMap; } diff --git a/src/internal/MapVerifierImpl.mts b/src/internal/MapVerifierImpl.mts index 6655031..01e6a39 100644 --- a/src/internal/MapVerifierImpl.mts +++ b/src/internal/MapVerifierImpl.mts @@ -13,9 +13,12 @@ import { /** * Default implementation of MapVerifier. + * + * @typeParam K - the type the map's keys + * @typeParam V - the type the map's values */ -class MapVerifierImpl extends AbstractObjectVerifier - implements MapVerifier +class MapVerifierImpl extends AbstractObjectVerifier, MapValidator, Map> + implements MapVerifier { /** * Creates a new MapVerifierImpl. @@ -23,7 +26,7 @@ class MapVerifierImpl extends AbstractObjectVerifier * @param validator - the validator to delegate to * @throws TypeError if validator is null or undefined */ - constructor(validator: MapValidator) + constructor(validator: MapValidator) { super(validator); } @@ -48,10 +51,10 @@ class MapVerifierImpl extends AbstractObjectVerifier keys() { const newValidator = this.validator.keys(); - return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; + return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; } - keysConsumer(consumer: (actual: ArrayVerifier) => void) + keysConsumer(consumer: (actual: ArrayVerifier) => void) { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.keys()); @@ -61,10 +64,10 @@ class MapVerifierImpl extends AbstractObjectVerifier values() { const newValidator = this.validator.values(); - return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; + return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; } - valuesConsumer(consumer: (actual: ArrayVerifier) => void) + valuesConsumer(consumer: (actual: ArrayVerifier) => void) { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.values()); @@ -74,10 +77,10 @@ class MapVerifierImpl extends AbstractObjectVerifier entries() { const newValidator = this.validator.entries(); - return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; + return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier<[K, V]>; } - entriesConsumer(consumer: (actual: ArrayVerifier) => void) + entriesConsumer(consumer: (actual: ArrayVerifier<[K, V]>) => void) { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.entries()); @@ -90,17 +93,12 @@ class MapVerifierImpl extends AbstractObjectVerifier return this.validationResult(() => new NumberVerifierImpl(newValidator)) as NumberVerifier; } - sizeConsumer(consumer: (actual: NumberVerifier) => void): MapVerifier + sizeConsumer(consumer: (actual: NumberVerifier) => void): MapVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.size()); return this; } - - getActual(): Map - { - return super.getActual() as Map; - } } export {MapVerifierImpl}; \ No newline at end of file diff --git a/src/internal/NumberValidatorImpl.mts b/src/internal/NumberValidatorImpl.mts index 2998983..f0228e0 100644 --- a/src/internal/NumberValidatorImpl.mts +++ b/src/internal/NumberValidatorImpl.mts @@ -21,7 +21,7 @@ class NumberValidatorImpl extends AbstractNumberValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: unknown, name: string, failures: ValidationFailure[]) + constructor(configuration: Configuration, actual: number | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); } diff --git a/src/internal/ObjectValidatorImpl.mts b/src/internal/ObjectValidatorImpl.mts index ce8585d..ae12ecb 100644 --- a/src/internal/ObjectValidatorImpl.mts +++ b/src/internal/ObjectValidatorImpl.mts @@ -7,9 +7,11 @@ import {AbstractObjectValidator} from "./internal.mjs"; /** * Default implementation of ObjectValidator. + * + * @typeParam T - the type the actual value */ -class ObjectValidatorImpl extends AbstractObjectValidator - implements ObjectValidator +class ObjectValidatorImpl extends AbstractObjectValidator, T> + implements ObjectValidator { /** * Creates a new ObjectValidator. @@ -21,13 +23,13 @@ class ObjectValidatorImpl extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: unknown, name: string, + constructor(configuration: Configuration, actual: T, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); } - protected getThis(): ObjectValidator + protected getThis(): ObjectValidator { return this; } diff --git a/src/internal/ObjectVerifierImpl.mts b/src/internal/ObjectVerifierImpl.mts index a6f0909..60c3054 100644 --- a/src/internal/ObjectVerifierImpl.mts +++ b/src/internal/ObjectVerifierImpl.mts @@ -1,31 +1,33 @@ import type { + ObjectValidator, + ObjectVerifier, + StringVerifier, ArrayVerifier, BooleanVerifier, - ClassVerifier, - InetAddressVerifier, - MapVerifier, NumberVerifier, - ObjectValidator, - ObjectVerifier, SetVerifier, - StringVerifier + MapVerifier, + InetAddressVerifier, + ClassVerifier } from "./internal.mjs"; import { + Objects, + StringVerifierImpl, ArrayVerifierImpl, BooleanVerifierImpl, - ClassVerifierImpl, - InetAddressVerifierImpl, - MapVerifierImpl, NumberVerifierImpl, - Objects, SetVerifierImpl, - StringVerifierImpl + MapVerifierImpl, + InetAddressVerifierImpl, + ClassVerifierImpl } from "./internal.mjs"; /** * Default implementation of ObjectVerifier. + * + * @typeParam T - the type the actual value */ -class ObjectVerifierImpl implements ObjectVerifier +class ObjectVerifierImpl, T> implements ObjectVerifier { protected readonly validator: V; @@ -46,17 +48,7 @@ class ObjectVerifierImpl implements ObjectVerifier return this; } - /** - * Ensures that the actual value is equal to a value. - * - * @param expected - the expected value - * @param name - (optional) the name of the expected value - * @returns the updated verifier - * @throws TypeError if name is null - * @throws RangeError if name is empty. - * If the actual value is not equal to expected. - */ - isEqualTo(expected: unknown, name?: string) + isEqualTo(expected: T, name?: string) { this.validator.isEqualTo(expected, name); return this.validationResult(() => this.getThis()); @@ -84,327 +76,171 @@ class ObjectVerifierImpl implements ObjectVerifier throw failure.createException(); } - /** - * Ensures that the actual value is not equal to a value. - * - * @param value - the value to compare to - * @param name - (optional) the name of the expected value - * @returns the updated verifier - * @throws TypeError if name is null - * @throws RangeError if name is empty. - * If the actual value is equal to value. - */ - isNotEqualTo(value: unknown, name?: string): ObjectVerifier + isNotEqualTo(value: T, name?: string): ObjectVerifier { this.validator.isNotEqualTo(value, name); return this.validationResult(() => this.getThis()); } - /** - * Ensures that the actual value is a primitive. To check if the actual value is an object, use - * isInstanceOf(Object). - * - * @returns the updated verifier - * @throws RangeError if the actual value is not a string, number, - * bigint, boolean, null, undefined, or - * symbol - */ - isPrimitive(): ObjectVerifier + isPrimitive(): ObjectVerifier { this.validator.isPrimitive(); return this.validationResult(() => this.getThis()); } - /** - * Ensures that typeof(actual) is equal to type. - * - * @param type - the expected - * typeof - * of the actual value - * @returns the updated verifier - * @throws RangeError if the typeof(actual) is not equal to type - */ - isTypeOf(type: string): ObjectVerifier + isTypeOf(type: string): ObjectVerifier { this.validator.isTypeOf(type); return this.validationResult(() => this.getThis()); } - /** - * Ensures that the actual value is an object that is an instance of the specified type. - * - * @param type - the type to compare to - * @returns the updated verifier - * @throws TypeError if type is undefined, null, anonymous function or an arrow function - * @throws RangeError if the actual value is not an instance of type - */ // eslint-disable-next-line @typescript-eslint/ban-types - isInstanceOf(type: Function): ObjectVerifier + isInstanceOf(type: Function): ObjectVerifier { this.validator.isInstanceOf(type); return this.validationResult(() => this.getThis()); } - /** - * Ensures that the actual value is null. - * - * @returns the updated verifier - * @throws RangeError if the actual value is not null - */ - isNull(): ObjectVerifier + isNull(): ObjectVerifier { this.validator.isNull(); return this.validationResult(() => this.getThis()); } - /** - * Ensures that the actual value is not null. - * - * @returns the updated verifier - * @throws RangeError if the actual value is null - */ - isNotNull(): ObjectVerifier + isNotNull(): ObjectVerifier { this.validator.isNotNull(); return this.validationResult(() => this.getThis()); } - /** - * Ensures that the actual value is defined. - * - * @returns the updated verifier - * @throws RangeError if the actual value is undefined - */ - isDefined(): ObjectVerifier + isDefined(): ObjectVerifier { this.validator.isDefined(); return this.validationResult(() => this.getThis()); } - /** - * Ensures that the actual value is undefined. - * - * @returns the updated verifier - * @throws RangeError if the actual value is not undefined - */ - isNotDefined(): ObjectVerifier + isNotDefined(): ObjectVerifier { this.validator.isNotDefined(); return this.validationResult(() => this.getThis()); } - /** - * Ensures that value is not undefined or null. - * - * @returns the updated verifier - * @throws TypeError if the value is undefined or null - */ - isSet(): ObjectVerifier + isSet(): ObjectVerifier { this.validator.isSet(); return this.validationResult(() => this.getThis()); } - /** - * Ensures that value is not undefined or null. - * - * @returns the updated verifier - * @throws TypeError if the value is not undefined or null - */ - isNotSet(): ObjectVerifier + isNotSet(): ObjectVerifier { this.validator.isNotSet(); return this.validationResult(() => this.getThis()); } - /** - * Returns the actual value. - * - * @returns the actual value - */ - getActual(): unknown + getActual(): T { - return this.validator.getActual(); + // The verifier is guaranteed to throw an exception if validation fails + return this.validator.getActual() as T; } - /** - * @returns a verifier for the object's string representation - */ asString(): StringVerifier { const newValidator = this.validator.asString(); return this.validationResult(() => new StringVerifierImpl(newValidator)) as StringVerifier; } - /** - * @param consumer - a function that accepts a {@link StringVerifier} for the string representation of the - * actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set - */ - asStringConsumer(consumer: (actual: StringVerifier) => void): ObjectVerifier + asStringConsumer(consumer: (actual: StringVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asString()); return this; } - /** - * @returns a verifier for the Array - * @throws TypeError if the actual value is not an Array - */ - asArray(): ArrayVerifier + asArray(): ArrayVerifier { const newValidator = this.validator.asArray(); - return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; + return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; } - /** - * @param consumer - a function that accepts a {@link ArrayVerifier} for the actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set. - * If the actual value is not an Array. - */ - asArrayConsumer(consumer: (actual: ArrayVerifier) => void): ObjectVerifier + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asArray()); return this; } - /** - * @returns a verifier for the boolean - * @throws TypeError if the actual value is not a boolean - */ asBoolean(): BooleanVerifier { const newValidator = this.validator.asBoolean(); return this.validationResult(() => new BooleanVerifierImpl(newValidator)) as BooleanVerifier; } - /** - * @param consumer - a function that accepts a {@link BooleanVerifier} for the actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set. - * If the actual value is not a boolean. - */ - asBooleanConsumer(consumer: (actual: BooleanVerifier) => void): ObjectVerifier + asBooleanConsumer(consumer: (actual: BooleanVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asBoolean()); return this; } - /** - * @returns a verifier for the number - * @throws TypeError if the actual value is not a number - */ asNumber(): NumberVerifier { const newValidator = this.validator.asNumber(); return this.validationResult(() => new NumberVerifierImpl(newValidator)) as NumberVerifier; } - /** - * @param consumer - a function that accepts a {@link NumberVerifier} for the actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set. - * If the actual value is not a number. - */ - asNumberConsumer(consumer: (actual: NumberVerifier) => void): ObjectVerifier + asNumberConsumer(consumer: (actual: NumberVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asNumber()); return this; } - /** - * @returns a verifier for the Set - * @throws TypeError if the actual value is not a Set - */ - asSet(): SetVerifier + asSet(): SetVerifier { const newValidator = this.validator.asSet(); - return this.validationResult(() => new SetVerifierImpl(newValidator)) as SetVerifier; + return this.validationResult(() => new SetVerifierImpl(newValidator)) as SetVerifier; } - /** - * @param consumer - a function that accepts a {@link SetVerifier} for the actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set. - * If the actual value is not a Set. - */ - asSetConsumer(consumer: (actual: SetVerifier) => void): ObjectVerifier + asSetConsumer(consumer: (actual: SetVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asSet()); return this; } - /** - * @returns a verifier for the Map - * @throws TypeError if the actual value is not a Map - */ - asMap(): MapVerifier + asMap(): MapVerifier { const newValidator = this.validator.asMap(); - return this.validationResult(() => new MapVerifierImpl(newValidator)) as MapVerifier; + return this.validationResult(() => new MapVerifierImpl(newValidator)) as MapVerifier; } - /** - * @param consumer - a function that accepts a {@link MapVerifier} for the actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set. - * If the actual value is not a Map. - */ - asMapConsumer(consumer: (actual: MapVerifier) => void): ObjectVerifier + asMapConsumer(consumer: (actual: MapVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asMap()); return this; } - /** - * @returns a verifier for the value's Internet address representation - * @throws RangeError if the actual value does not contain a valid Internet address format - */ asInetAddress(): InetAddressVerifier { const newValidator = this.validator.asInetAddress(); return this.validationResult(() => new InetAddressVerifierImpl(newValidator)) as InetAddressVerifier; } - /** - * @param consumer - a function that accepts an {@link InetAddressVerifier} for the value's Internet - * address representation - * @returns the updated verifier - * @throws TypeError if consumer is not set - * @throws RangeError if the actual value does not contain a valid Internet address format - */ - asInetAddressConsumer(consumer: (input: InetAddressVerifier) => void): ObjectVerifier + asInetAddressConsumer(consumer: (input: InetAddressVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asInetAddress()); return this; } - /** - * @returns a verifier for the object's class representation - * @throws TypeError if the actual value is not a Function - */ asClass(): ClassVerifier { const newValidator = this.validator.asClass(); return this.validationResult(() => new ClassVerifierImpl(newValidator)) as ClassVerifier; } - /** - * @param consumer - a function that accepts a {@link ClassVerifier} for the class representation of the - * actual value - * @returns the updated verifier - * @throws TypeError if consumer is not set - */ - asClassConsumer(consumer: (actual: ClassVerifier) => void): ObjectVerifier + asClassConsumer(consumer: (actual: ClassVerifier) => void): ObjectVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asClass()); diff --git a/src/internal/SetValidatorImpl.mts b/src/internal/SetValidatorImpl.mts index bd686f1..4660e0a 100644 --- a/src/internal/SetValidatorImpl.mts +++ b/src/internal/SetValidatorImpl.mts @@ -8,8 +8,6 @@ import { AbstractObjectValidator, ArrayValidatorImpl, Objects, - ObjectValidatorImpl, - ObjectVerifierImpl, Pluralizer, SizeValidatorImpl, ValidationFailure @@ -18,10 +16,10 @@ import { /** * Default implementation of SetValidator. */ -class SetValidatorImpl extends AbstractObjectValidator - implements SetValidator +class SetValidatorImpl extends AbstractObjectValidator, Set> + implements SetValidator { - private readonly actualSet: void | Set; + private readonly actualSet: Set | undefined; /** * Creates a new SetValidatorImpl. @@ -33,7 +31,7 @@ class SetValidatorImpl extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: void | Set, name: string, + constructor(configuration: Configuration, actual: Set | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); @@ -50,9 +48,8 @@ class SetValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (actualAsNotVoid.size !== 0) + const actualAsDefined: Set = this.actualSet as Set; + if (actualAsDefined.size !== 0) { const failure = new ValidationFailure(this.config, RangeError, this.name + " must be empty."). addContext("Actual", this.actualSet); @@ -66,8 +63,8 @@ class SetValidatorImpl extends AbstractObjectValidator if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - const actualAsNotVoid: Set = this.actualSet as Set; - if (actualAsNotVoid.size === 0) + const actualAsDefined: Set = this.actualSet as Set; + if (actualAsDefined.size === 0) { const failure = new ValidationFailure(this.config, RangeError, this.name + " may not be empty"); @@ -76,16 +73,15 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - contains(expected: unknown, name?: string) + contains(expected: E, name?: string) { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (!actualAsNotVoid.has(expected)) + const actualAsDefined: Set = this.actualSet as Set; + if (!actualAsDefined.has(expected)) { let failure; if (name) @@ -106,30 +102,17 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - /** - * @param value - the value - * @param name - the name of the value - * @returns the value converted to a Set - */ - private convertToSet(value: unknown[] | Set, name: string) + containsExactly(expected: E[] | Set, name?: string): SetValidator { - const expectedValidator = new ObjectValidatorImpl(this.config, value, name, this.failures); - const expectedVerifier = new ObjectVerifierImpl(expectedValidator); - return expectedVerifier.asSet().getActual(); - } - - containsExactly(expected: unknown[] | Set, name?: string): SetValidator - { - const expectedAsSet = this.convertToSet(expected, "expected"); + const expectedAsSet = new Set(expected); if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - const missing = new Set([...expectedAsSet].filter(x => !actualAsNotVoid.has(x))); - const unwanted = new Set([...actualAsNotVoid].filter(x => !expectedAsSet.has(x))); + const actualAsDefined: Set = this.actualSet as Set; + const missing = new Set([...expectedAsSet].filter(x => !actualAsDefined.has(x))); + const unwanted = new Set([...actualAsDefined].filter(x => !expectedAsSet.has(x))); if (missing.size !== 0 || unwanted.size !== 0) { let failure; @@ -155,17 +138,16 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - containsAny(expected: unknown[] | Set, name?: string): SetValidator + containsAny(expected: E[] | Set, name?: string): SetValidator { - const expectedAsSet = this.convertToSet(expected, "expected"); + const expectedAsSet = new Set(expected); if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (!SetValidatorImpl.actualContainsAny(actualAsNotVoid, expectedAsSet)) + const actualAsDefined: Set = this.actualSet as Set; + if (!this.actualContainsAny(actualAsDefined, expectedAsSet)) { let failure; if (name) @@ -186,19 +168,18 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - containsAll(expected: unknown[] | Set, name?: string): SetValidator + containsAll(expected: E[] | Set, name?: string): SetValidator { - const expectedAsSet = this.convertToSet(expected, "expected"); + const expectedAsSet = new Set(expected); if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (!SetValidatorImpl.actualContainsAll(actualAsNotVoid, expectedAsSet)) + const actualAsDefined: Set = this.actualSet as Set; + if (!this.actualContainsAll(actualAsDefined, expectedAsSet)) { - const missing = new Set([...expectedAsSet].filter(x => !actualAsNotVoid.has(x))); + const missing = new Set([...expectedAsSet].filter(x => !actualAsDefined.has(x))); let failure; if (name) { @@ -220,16 +201,15 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - doesNotContain(entry: unknown, name?: string): SetValidator + doesNotContain(entry: E, name?: string): SetValidator { if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (actualAsNotVoid.has(entry)) + const actualAsDefined: Set = this.actualSet as Set; + if (actualAsDefined.has(entry)) { let failure; if (name) @@ -250,17 +230,16 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - doesNotContainAny(elements: unknown[] | Set, name?: string): SetValidator + doesNotContainAny(elements: E[] | Set, name?: string): SetValidator { - const elementsAsSet = this.convertToSet(elements, "elements"); + const elementsAsSet = new Set(elements); if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (SetValidatorImpl.actualContainsAny(actualAsNotVoid, elementsAsSet)) + const actualAsDefined: Set = this.actualSet as Set; + if (this.actualContainsAny(actualAsDefined, elementsAsSet)) { let failure; if (name) @@ -281,19 +260,18 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - doesNotContainAll(elements: unknown[] | Set, name?: string): SetValidator + doesNotContainAll(elements: E[] | Set, name?: string): SetValidator { - const elementsAsSet = this.convertToSet(elements, "elements"); + const elementsAsSet = new Set(elements); if (typeof (name) !== "undefined") Objects.requireThatStringIsNotEmpty(name, "name"); if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - - const actualAsNotVoid: Set = this.actualSet as Set; - if (SetValidatorImpl.actualContainsAll(actualAsNotVoid, elementsAsSet)) + const actualAsDefined: Set = this.actualSet as Set; + if (this.actualContainsAll(actualAsDefined, elementsAsSet)) { - const missing = new Set([...elementsAsSet].filter(x => !actualAsNotVoid.has(x))); + const missing = new Set([...elementsAsSet].filter(x => !actualAsDefined.has(x))); let failure; if (name) { @@ -317,7 +295,7 @@ class SetValidatorImpl extends AbstractObjectValidator size(): NumberValidator { - let value: void | Set; + let value: Set | undefined; let size: number; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) { @@ -326,14 +304,14 @@ class SetValidatorImpl extends AbstractObjectValidator } else { - value = this.actualSet as Set; + value = this.actualSet as Set; size = value.size; } return new SizeValidatorImpl(this.config, value, this.name, size, this.name + ".size", Pluralizer.ELEMENT, this.failures); } - sizeConsumer(consumer: (actual: NumberValidator) => void): SetValidator + sizeConsumer(consumer: (actual: NumberValidator) => void): SetValidator { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -341,22 +319,24 @@ class SetValidatorImpl extends AbstractObjectValidator return this; } - asArray(): ArrayValidator + asArray(): ArrayValidator; + asArray(): ArrayValidator { - let value: void | unknown[]; + let value: E[] | undefined; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else { - const actualAsNotVoid = this.actual as unknown[]; - value = Array.from(actualAsNotVoid.values()); + const actualAsDefined = this.actual as Set; + value = Array.from(actualAsDefined.values()); } - return new ArrayValidatorImpl(this.config, value, this.name + ".asArray()", + return new ArrayValidatorImpl(this.config, value, this.name + ".asArray()", Pluralizer.ELEMENT, this.failures); } - asArrayConsumer(consumer: (actual: ArrayValidator) => void): SetValidator + asArrayConsumer(consumer: (input: ArrayValidator) => void): S; + asArrayConsumer(consumer: (actual: ArrayValidator) => void): SetValidator { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asArray()); @@ -368,7 +348,7 @@ class SetValidatorImpl extends AbstractObjectValidator * @param expected - a set of expected elements * @returns true if actual contains any of the expected elements */ - private static actualContainsAny(actual: Set, expected: Set): boolean + private actualContainsAny(actual: Set, expected: Set): boolean { for (const entry of expected) { @@ -383,7 +363,7 @@ class SetValidatorImpl extends AbstractObjectValidator * @param expected - a Set of expected values * @returns true if actual contains all the expected elements */ - private static actualContainsAll(actual: Set, expected: Set): boolean + private actualContainsAll(actual: Set, expected: Set): boolean { for (const entry of expected) { @@ -393,7 +373,13 @@ class SetValidatorImpl extends AbstractObjectValidator return true; } - getActual(): void | Set + asSet(): SetValidator; + asSet(): SetValidator + { + return this; + } + + getActual(): Set | undefined { return this.actualSet; } diff --git a/src/internal/SetVerifierImpl.mts b/src/internal/SetVerifierImpl.mts index 29436c4..5d7d65a 100644 --- a/src/internal/SetVerifierImpl.mts +++ b/src/internal/SetVerifierImpl.mts @@ -13,9 +13,11 @@ import { /** * Default implementation of SetVerifier. + * + * @typeParam E - the type the array elements */ -class SetVerifierImpl extends AbstractObjectVerifier - implements SetVerifier +class SetVerifierImpl extends AbstractObjectVerifier, SetValidator, Set> + implements SetVerifier { /** * Creates a new SetVerifierImpl. @@ -23,7 +25,7 @@ class SetVerifierImpl extends AbstractObjectVerifier * @param validator - the validator to delegate to * @throws TypeError if validator is null or undefined */ - constructor(validator: SetValidator) + constructor(validator: SetValidator) { super(validator); } @@ -45,43 +47,43 @@ class SetVerifierImpl extends AbstractObjectVerifier return this.validationResult(() => this.getThis()); } - contains(expected: unknown, name?: string) + contains(expected: E, name?: string) { this.validator.contains(expected, name); return this.validationResult(() => this.getThis()); } - containsExactly(expected: unknown[] | Set, name?: string) + containsExactly(expected: E[] | Set, name?: string) { this.validator.containsExactly(expected, name); return this.validationResult(() => this.getThis()); } - containsAny(expected: unknown[] | Set, name?: string) + containsAny(expected: E[] | Set, name?: string) { this.validator.containsAny(expected, name); return this.validationResult(() => this.getThis()); } - containsAll(expected: unknown[] | Set, name?: string) + containsAll(expected: E[] | Set, name?: string) { this.validator.containsAll(expected, name); return this.validationResult(() => this.getThis()); } - doesNotContain(entry: unknown, name?: string) + doesNotContain(entry: E, name?: string) { this.validator.doesNotContain(entry, name); return this.validationResult(() => this.getThis()); } - doesNotContainAny(elements: unknown[] | Set, name?: string): SetVerifier + doesNotContainAny(elements: E[] | Set, name?: string): SetVerifier { this.validator.doesNotContainAny(elements, name); return this.validationResult(() => this.getThis()); } - doesNotContainAll(elements: unknown[] | Set, name?: string): SetVerifier + doesNotContainAll(elements: E[] | Set, name?: string): SetVerifier { this.validator.doesNotContainAll(elements, name); return this.validationResult(() => this.getThis()); @@ -93,29 +95,32 @@ class SetVerifierImpl extends AbstractObjectVerifier return this.validationResult(() => new NumberVerifierImpl(newValidator)) as NumberVerifier; } - sizeConsumer(consumer: (actual: NumberVerifier) => void): SetVerifier + sizeConsumer(consumer: (actual: NumberVerifier) => void): SetVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.size()); return this; } - asArray(): ArrayVerifier + asArray(): ArrayVerifier; + asArray(): ArrayVerifier { const newValidator = this.validator.asArray(); - return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; + return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; } - asArrayConsumer(consumer: (actual: ArrayVerifier) => void): SetVerifier + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): SetVerifier; + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): SetVerifier { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asArray()); return this; } - getActual(): Set + asSet(): SetVerifier; + asSet(): SetVerifier { - return super.getActual() as Set; + return this; } } diff --git a/src/internal/SizeValidatorImpl.mts b/src/internal/SizeValidatorImpl.mts index 1830f66..20a1410 100644 --- a/src/internal/SizeValidatorImpl.mts +++ b/src/internal/SizeValidatorImpl.mts @@ -15,9 +15,9 @@ import { class SizeValidatorImpl extends NumberValidatorImpl implements NumberValidator { - private readonly collection: void | unknown[] | Set | Map | string; + private readonly collection: unknown; private readonly collectionName: string; - private readonly size: void | number; + private readonly size: number | undefined; private readonly pluralizer: Pluralizer; /** @@ -34,9 +34,8 @@ class SizeValidatorImpl extends NumberValidatorImpl * undefined or null. If containerName or sizeName are not a string. * @throws RangeError if containerName or sizeName are empty */ - constructor(configuration: Configuration, - collection: void | unknown[] | Set | Map | string, - collectionName: string, size: void | number, sizeName: string, pluralizer: Pluralizer, + constructor(configuration: Configuration, collection: unknown, + collectionName: string, size: number | undefined, sizeName: string, pluralizer: Pluralizer, failures: ValidationFailure[]) { super(configuration, size, sizeName, failures); @@ -93,8 +92,8 @@ class SizeValidatorImpl extends NumberValidatorImpl } failure.addContext("Actual", this.actual); - const sizeAsNotVoid = this.size as number; - if (sizeAsNotVoid > 0) + const sizeAsDefined = this.size as number; + if (sizeAsDefined > 0) failure.addContext(this.collectionName, this.collection); this.failures.push(failure); } @@ -145,15 +144,15 @@ class SizeValidatorImpl extends NumberValidatorImpl if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - const sizeAsNotVoid = this.size as number; - if (sizeAsNotVoid < startInclusive || sizeAsNotVoid >= endExclusive) + const sizeAsDefined = this.size as number; + if (sizeAsDefined < startInclusive || sizeAsDefined >= endExclusive) { const failure = new ValidationFailure(this.config, RangeError, this.collectionName + " must contain [" + startInclusive + ", " + endExclusive + ") " + this.pluralizer.nameOf(2) + "."). - addContext("Actual", sizeAsNotVoid); + addContext("Actual", sizeAsDefined); - if (sizeAsNotVoid > 0) + if (sizeAsDefined > 0) failure.addContext(this.collectionName, this.collection); this.failures.push(failure); } @@ -173,22 +172,22 @@ class SizeValidatorImpl extends NumberValidatorImpl if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) return this; - const sizeAsNotVoid = this.size as number; - if (sizeAsNotVoid < startInclusive || sizeAsNotVoid > endInclusive) + const sizeAsDefined = this.size as number; + if (sizeAsDefined < startInclusive || sizeAsDefined > endInclusive) { const failure = new ValidationFailure(this.config, RangeError, this.collectionName + " must contain [" + startInclusive + ", " + endInclusive + "] " + this.pluralizer.nameOf(2) + "."). - addContext("Actual", sizeAsNotVoid); + addContext("Actual", sizeAsDefined); - if (sizeAsNotVoid > 0) + if (sizeAsDefined > 0) failure.addContext(this.collectionName, this.collection); this.failures.push(failure); } return this; } - getActual(): void | number + getActual(): number | undefined { return this.size; } diff --git a/src/internal/StringValidatorImpl.mts b/src/internal/StringValidatorImpl.mts index e0cc797..81b9a8b 100644 --- a/src/internal/StringValidatorImpl.mts +++ b/src/internal/StringValidatorImpl.mts @@ -14,7 +14,7 @@ import { /** * Default implementation of StringValidator. */ -class StringValidatorImpl extends AbstractObjectValidator +class StringValidatorImpl extends AbstractObjectValidator implements StringValidator { private actualString: string; @@ -29,7 +29,7 @@ class StringValidatorImpl extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - constructor(configuration: Configuration, actual: unknown, name: string, failures: ValidationFailure[]) + constructor(configuration: Configuration, actual: string | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); this.actualString = actual as string; @@ -183,7 +183,7 @@ class StringValidatorImpl extends AbstractObjectValidator length(): NumberValidator { - let value: void | number; + let value: number | undefined; if (this.failures.length > 0 || !this.requireThatActualIsDefinedAndNotNull()) value = undefined; else @@ -202,12 +202,6 @@ class StringValidatorImpl extends AbstractObjectValidator asString(): StringValidator { - if (this.failures.length > 0) - return this; - if (typeof (this.actualString) === "undefined") - return new StringValidatorImpl(this.config, "undefined", this.name, this.failures); - if (this.actualString === null) - return new StringValidatorImpl(this.config, "null", this.name, this.failures); return this; } diff --git a/src/internal/StringVerifierImpl.mts b/src/internal/StringVerifierImpl.mts index 304c857..b41d6e2 100644 --- a/src/internal/StringVerifierImpl.mts +++ b/src/internal/StringVerifierImpl.mts @@ -12,7 +12,7 @@ import { /** * Default implementation of StringVerifier. */ -class StringVerifierImpl extends AbstractObjectVerifier +class StringVerifierImpl extends AbstractObjectVerifier implements StringVerifier { /** @@ -122,11 +122,6 @@ class StringVerifierImpl extends AbstractObjectVerifier extends AbstractObjectValidator +abstract class AbstractNumberValidator extends AbstractObjectValidator implements ExtensibleNumberValidator { private readonly actualNumber: number; @@ -28,7 +28,7 @@ abstract class AbstractNumberValidator extends AbstractObjectValidator * @throws TypeError if configuration or name are null or undefined * @throws RangeError if name is empty */ - protected constructor(configuration: Configuration, actual: unknown, name: string, + protected constructor(configuration: Configuration, actual: number | undefined, name: string, failures: ValidationFailure[]) { super(configuration, actual, name, failures); @@ -341,9 +341,9 @@ abstract class AbstractNumberValidator extends AbstractObjectValidator return this.getThis(); } - getActual(): void | number + getActual(): number | undefined { - return super.getActual() as void | number; + return super.getActual(); } } diff --git a/src/internal/extension/AbstractNumberVerifier.mts b/src/internal/extension/AbstractNumberVerifier.mts index 732d774..bc028aa 100644 --- a/src/internal/extension/AbstractNumberVerifier.mts +++ b/src/internal/extension/AbstractNumberVerifier.mts @@ -10,7 +10,7 @@ import {AbstractObjectVerifier} from "../internal.mjs"; * @typeParam S - the type of validator returned by the methods */ abstract class AbstractNumberVerifier> - extends AbstractObjectVerifier + extends AbstractObjectVerifier implements ExtensibleNumberVerifier { isNegative(): S @@ -108,11 +108,6 @@ abstract class AbstractNumberVerifier> this.validator.isNotFinite(); return this.validationResult(() => this.getThis()); } - - getActual(): number - { - return super.getActual() as number; - } } export {AbstractNumberVerifier}; \ No newline at end of file diff --git a/src/internal/extension/AbstractObjectValidator.mts b/src/internal/extension/AbstractObjectValidator.mts index bec99f4..3301fa7 100644 --- a/src/internal/extension/AbstractObjectValidator.mts +++ b/src/internal/extension/AbstractObjectValidator.mts @@ -1,41 +1,42 @@ import isEqual from "lodash/isEqual.js"; import type { - ArrayValidator, - BooleanValidator, - ClassValidator, ContextLine, ExtensibleObjectValidator, - InetAddressValidator, - MapValidator, + StringValidator, + ArrayValidator, + BooleanValidator, NumberValidator, SetValidator, - StringValidator + MapValidator, + InetAddressValidator, + ClassValidator } from "../internal.mjs"; import { - ArrayValidatorImpl, - BooleanValidatorImpl, - ClassValidatorImpl, Configuration, ContextGenerator, - InetAddressValidatorImpl, - MapValidatorImpl, - NumberValidatorImpl, Objects, - Pluralizer, - SetValidatorImpl, StringValidatorImpl, - ValidationFailure + ValidationFailure, + ArrayValidatorImpl, + BooleanValidatorImpl, + NumberValidatorImpl, + SetValidatorImpl, + MapValidatorImpl, + InetAddressValidatorImpl, + ClassValidatorImpl, + Pluralizer } from "../internal.mjs"; /** * Extensible implementation of ExtensibleObjectValidator. * * @typeParam S - the type of validator returned by the methods + * @typeParam T - the type the actual value */ -abstract class AbstractObjectValidator implements ExtensibleObjectValidator +abstract class AbstractObjectValidator implements ExtensibleObjectValidator { protected readonly config: Configuration; - protected actual: unknown; + protected actual: T | undefined; protected readonly name: string; protected readonly failures: ValidationFailure[]; @@ -54,7 +55,7 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidatorconfiguration or name are null or undefined * @throws RangeError if name is empty */ - protected constructor(configuration: Configuration, actual: unknown, name: string, failures: ValidationFailure[]) + protected constructor(configuration: Configuration, actual: T | undefined, name: string, failures: ValidationFailure[]) { Objects.assertThatInstanceOf(configuration, "configuration", Configuration); Objects.verifyName(name, "name"); @@ -64,7 +65,7 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator implements ExtensibleObjectValidator implements ExtensibleObjectValidator implements ExtensibleObjectValidator implements ExtensibleObjectValidator 0) value = undefined; else @@ -272,7 +273,7 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator StringValidator): S + asStringConsumer(consumer: (actual: StringValidator) => StringValidator): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -280,14 +281,14 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator(): ArrayValidator { if (this.failures.length === 0) { const typeOfActual = Objects.getTypeInfo(this.actual); if (typeOfActual.type === "array") { - return new ArrayValidatorImpl(this.config, this.actual as unknown[], this.name, Pluralizer.ELEMENT, + return new ArrayValidatorImpl(this.config, this.actual as E[], this.name, Pluralizer.ELEMENT, this.failures); } @@ -297,10 +298,10 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator(this.config, undefined, this.name, Pluralizer.ELEMENT, this.failures); } - asArrayConsumer(consumer: (input: ArrayValidator) => void): S + asArrayConsumer(consumer: (input: ArrayValidator) => void): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -364,28 +365,28 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator(): SetValidator { if (this.failures.length === 0) { const typeOfActual = Objects.getTypeInfo(this.actual); if (typeOfActual.type === "array") { - return new SetValidatorImpl(this.config, new Set(this.actual as unknown[]), this.name, + return new SetValidatorImpl(this.config, new Set(this.actual as E[]), this.name, this.failures); } else if (typeOfActual.type === "object" && typeOfActual.name === "Set") - return new SetValidatorImpl(this.config, this.actual as Set, this.name, this.failures); + return new SetValidatorImpl(this.config, this.actual as Set, this.name, this.failures); const failure = new ValidationFailure(this.config, TypeError, this.name + " must be a Set."). addContext("Actual", this.config.convertToString(this.actual)). addContext("Type", typeOfActual); this.failures.push(failure); } - return new SetValidatorImpl(this.config, undefined, this.name, this.failures); + return new SetValidatorImpl(this.config, undefined, this.name, this.failures); } - asSetConsumer(consumer: (actual: SetValidator) => void): S + asSetConsumer(consumer: (actual: SetValidator) => void): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -393,23 +394,23 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator(): MapValidator { if (this.failures.length === 0) { const typeOfActual = Objects.getTypeInfo(this.actual); if (typeOfActual.type === "object" && typeOfActual.name === "Map") - return new MapValidatorImpl(this.config, this.actual, this.name, this.failures); + return new MapValidatorImpl(this.config, this.actual as Map, this.name, this.failures); const failure = new ValidationFailure(this.config, TypeError, this.name + " must be a Map."). addContext("Actual", this.config.convertToString(this.actual)). addContext("Type", typeOfActual); this.failures.push(failure); } - return new MapValidatorImpl(this.config, undefined, this.name, this.failures); + return new MapValidatorImpl(this.config, undefined, this.name, this.failures); } - asMapConsumer(consumer: (input: MapValidator) => void): S + asMapConsumer(consumer: (input: MapValidator) => void): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); if (this.failures.length === 0) @@ -609,14 +610,14 @@ abstract class AbstractObjectValidator implements ExtensibleObjectValidator implements ExtensibleObjectValidatorExtensibleObjectVerifier. * * @typeParam S - the type of validator returned by the methods + * @typeParam T - the type the actual value */ -abstract class AbstractObjectVerifier> - implements ExtensibleObjectVerifier +abstract class AbstractObjectVerifier, T> + implements ExtensibleObjectVerifier { protected readonly validator: V; @@ -49,7 +50,7 @@ abstract class AbstractObjectVerifier> */ protected abstract getThis(): S; - isEqualTo(expected: unknown, name?: string): S + isEqualTo(expected: T, name?: string): S { this.validator.isEqualTo(expected, name); return this.validationResult(() => this.getThis()); @@ -70,7 +71,7 @@ abstract class AbstractObjectVerifier> throw failure.createException(); } - isNotEqualTo(value: unknown, name?: string): S + isNotEqualTo(value: T, name?: string): S { this.validator.isNotEqualTo(value, name); return this.validationResult(() => this.getThis()); @@ -131,9 +132,10 @@ abstract class AbstractObjectVerifier> return this.validationResult(() => this.getThis()); } - getActual(): unknown + getActual(): T { - return this.validator.getActual(); + // The verifier is guaranteed to throw an exception if validation fails + return this.validator.getActual() as T; } asString(): StringVerifier @@ -149,13 +151,13 @@ abstract class AbstractObjectVerifier> return this.getThis(); } - asArray(): ArrayVerifier + asArray(): ArrayVerifier { const newValidator = this.validator.asArray(); - return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; + return this.validationResult(() => new ArrayVerifierImpl(newValidator)) as ArrayVerifier; } - asArrayConsumer(consumer: (actual: ArrayVerifier) => void): S + asArrayConsumer(consumer: (actual: ArrayVerifier) => void): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asArray()); @@ -188,26 +190,26 @@ abstract class AbstractObjectVerifier> return this.getThis(); } - asSet(): SetVerifier + asSet(): SetVerifier { const newValidator = this.validator.asSet(); - return this.validationResult(() => new SetVerifierImpl(newValidator)) as SetVerifier; + return this.validationResult(() => new SetVerifierImpl(newValidator)) as SetVerifier; } - asSetConsumer(consumer: (actual: SetVerifier) => void): S + asSetConsumer(consumer: (actual: SetVerifier) => void): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asSet()); return this.getThis(); } - asMap(): MapVerifier + asMap(): MapVerifier { const newValidator = this.validator.asMap(); - return this.validationResult(() => new MapVerifierImpl(newValidator)) as MapVerifier; + return this.validationResult(() => new MapVerifierImpl(newValidator)) as MapVerifier; } - asMapConsumer(consumer: (actual: MapVerifier) => void): S + asMapConsumer(consumer: (actual: MapVerifier) => void): S { Objects.requireThatValueIsDefinedAndNotNull(consumer, "consumer"); consumer(this.asMap()); diff --git a/src/internal/internal.mts b/src/internal/internal.mts index 3e1fb90..34ff370 100644 --- a/src/internal/internal.mts +++ b/src/internal/internal.mts @@ -85,7 +85,6 @@ import {Node16MillionColors} from "./diff/Node16MillionColors.mjs"; import {Node256Colors} from "./diff/Node256Colors.mjs"; import type {GlobalConfiguration} from "../GlobalConfiguration.mjs"; import {IllegalStateError} from "./IllegalStateError.mjs"; -import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; import {Maps} from "./Maps.mjs"; import {SizeValidatorImpl} from "./SizeValidatorImpl.mjs"; import {Pluralizer} from "./Pluralizer.mjs"; @@ -120,7 +119,6 @@ export InetAddressValidatorImpl, InetAddressVerifierImpl, MainGlobalConfiguration, - TestGlobalConfiguration, Maps, MapValidatorImpl, MapVerifierImpl, diff --git a/test/ArrayTest.mts b/test/ArrayTest.mts index f126e69..83151c0 100644 --- a/test/ArrayTest.mts +++ b/test/ArrayTest.mts @@ -6,9 +6,9 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; -import {Requirements} from "../src/index.mjs"; + Requirements +} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/BooleanTest.mts b/test/BooleanTest.mts index 24018d1..9d31b5f 100644 --- a/test/BooleanTest.mts +++ b/test/BooleanTest.mts @@ -6,9 +6,9 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; -import {Requirements} from "../src/index.mjs"; + Requirements +} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/ClassTest.mts b/test/ClassTest.mts index 0057b28..9f7d72e 100644 --- a/test/ClassTest.mts +++ b/test/ClassTest.mts @@ -6,9 +6,9 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; -import {Requirements} from "../src/index.mjs"; + Requirements +} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/ConfigurationTest.mts b/test/ConfigurationTest.mts index cb18ab7..eafa63d 100644 --- a/test/ConfigurationTest.mts +++ b/test/ConfigurationTest.mts @@ -6,9 +6,9 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; -import {Requirements} from "../src/Requirements.mjs"; + Requirements +} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/DiffTest.mts b/test/DiffTest.mts index c714238..7bd0564 100644 --- a/test/DiffTest.mts +++ b/test/DiffTest.mts @@ -11,10 +11,10 @@ import { Node16MillionColors, Node256Colors, TerminalEncoding, - TestGlobalConfiguration, TextOnly } from "../src/internal/internal.mjs"; -import {Requirements} from "../src/Requirements.mjs"; +import {Requirements} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; suite("DiffTest", () => diff --git a/test/InetAddressTest.mts b/test/InetAddressTest.mts index 4925c73..40af1e8 100644 --- a/test/InetAddressTest.mts +++ b/test/InetAddressTest.mts @@ -6,9 +6,9 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; -import {Requirements} from "../src/Requirements.mjs"; + Requirements +} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/MapTest.mts b/test/MapTest.mts index b499c0f..d2a53f3 100644 --- a/test/MapTest.mts +++ b/test/MapTest.mts @@ -1,14 +1,14 @@ -import {Requirements} from "../src/index.mjs"; +import { + Requirements, + Configuration, + TerminalEncoding +} from "../src/index.mjs"; import { suite, test } from "mocha"; import {assert} from "chai"; -import { - Configuration, - TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/NumberTest.mts b/test/NumberTest.mts index 676acea..a153923 100644 --- a/test/NumberTest.mts +++ b/test/NumberTest.mts @@ -1,14 +1,14 @@ -import {Requirements} from "../src/index.mjs"; +import { + Requirements, + Configuration, + TerminalEncoding +} from "../src/index.mjs"; import { suite, test } from "mocha"; import {assert} from "chai"; -import { - Configuration, - TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/ObjectTest.mts b/test/ObjectTest.mts index 20085a6..34d7525 100644 --- a/test/ObjectTest.mts +++ b/test/ObjectTest.mts @@ -7,14 +7,17 @@ import type {ObjectValidator} from "../src/internal/internal.mjs"; import { Configuration, ObjectVerifierImpl, - TerminalEncoding, - TestGlobalConfiguration + TerminalEncoding } from "../src/internal/internal.mjs"; import {Requirements} from "../src/index.mjs"; +import {TypeScriptCompiler} from "./TypescriptCompiler.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; +import * as os from "os"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); const requirements = new Requirements(configuration); +const compiler = new TypeScriptCompiler(); suite("ObjectTest", () => { @@ -24,7 +27,7 @@ suite("ObjectTest", () => { let actual: undefined; /* eslint-disable no-new */ - new ObjectVerifierImpl(actual as unknown as ObjectValidator); + new ObjectVerifierImpl(actual as unknown as ObjectValidator); /* eslint-enable no-new */ }, TypeError); }); @@ -68,12 +71,15 @@ suite("ObjectTest", () => test("isEqual_sameToStringDifferentTypes", () => { - assert.throws(function() - { - const actual = "null"; - requirements.requireThat(actual, "actual").isEqualTo(null); - }, RangeError); - }); + const code = + `import {requireThat} from "./target/publish/node/index.mjs"; + + const actual = "null" + requireThat(actual, "actual").isEqualTo(null);`; + const messages = compiler.compile(code); + assert.equal(messages, "test.mts(4,44): error TS2345: Argument of type 'null' is not assignable " + + "to parameter of type 'string'." + os.EOL); + }).timeout(5000); test("isEqual_nullToNull", () => { @@ -83,25 +89,32 @@ suite("ObjectTest", () => test("isEqualTo_nullToNotNull", () => { - assert.throws(function() - { + const code = + `import {requireThat} from "./target/publish/node/index.mjs"; + const actual = null; - requirements.requireThat(actual, "actual").isEqualTo("expected"); - }, RangeError); - }); + requireThat(actual, "actual").isEqualTo("expected");`; + const messages = compiler.compile(code); + assert.equal(messages, "test.mts(4,44): error TS2345: Argument of type '\"expected\"' is not " + + "assignable to parameter of type 'null'." + os.EOL); + }).timeout(5000); test("isEqualTo_notNullToNull", () => { - assert.throws(function() - { + const code = + `import {requireThat} from "./target/publish/node/index.mjs"; + const actual = "actual"; - requirements.requireThat(actual, "actual").isEqualTo(null); - }, RangeError); - }); + requireThat(actual, "actual").isEqualTo(null);`; + const messages = compiler.compile(code); + assert.equal(messages, "test.mts(4,44): error TS2345: Argument of type 'null' is not assignable " + + "to parameter of type 'string'." + os.EOL); + }).timeout(5000); test("isNotEqualTo", () => { - requirements.requireThat("actualValue", "actual").isNotEqualTo("expectedValue"); + const actual = "actualValue"; + requirements.requireThat(actual, "actual").isNotEqualTo("expectedValue"); }); test("isNotEqualTo_False", () => diff --git a/test/RequirementsTest.mts b/test/RequirementsTest.mts index 5eac5b9..5fc9b34 100644 --- a/test/RequirementsTest.mts +++ b/test/RequirementsTest.mts @@ -1,14 +1,14 @@ -import {Requirements} from "../src/index.mjs"; +import { + Requirements, + Configuration, + TerminalEncoding +} from "../src/index.mjs"; import { suite, test } from "mocha"; import {assert} from "chai"; -import { - Configuration, - TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); @@ -25,31 +25,31 @@ suite("RequirementsTest", () => test("assertThatArray", () => { const actual = [1, 2, 3]; - requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo("expected")); + requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo(actual, "expected")); }); test("assertThatNumber", () => { const actual = 5; - requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo("expected")); + requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo(actual, "expected")); }); test("assertThatSet", () => { const actual = new Set([1, 2, 3]); - requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo("expected")); + requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo(actual, "expected")); }); test("assertThatMap", () => { const actual = new Map([[1, 2], [2, 3]]); - requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo("expected")); + requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo(actual, "expected")); }); test("assertThatUrl", () => { const actual = new URL("http://www.google.com/"); - requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo("expected")); + requirements.assertThat(r => r.requireThat(actual, "actual").isEqualTo(actual, "expected")); }); test("withAssertionsEnabled.assertThatObject", () => @@ -110,7 +110,7 @@ suite("RequirementsTest", () => const actual = 12345; const getActual = requirements.copy().withAssertionsEnabled().assertThatAndReturn(r => r.requireThat(actual, "actual").getActual()); - requirements.requireThat(actual, "actual").isEqualTo(getActual, "getActual()"); + requirements.requireThat(actual, "actual").isEqualTo(getActual as number, "getActual()"); }); test("assertThat.getActual_assertionsDisabled", () => @@ -118,7 +118,7 @@ suite("RequirementsTest", () => const actual = 12345; const getActual = requirements.copy().withAssertionsDisabled().assertThatAndReturn(r => r.requireThat(actual, "actual").getActual()); - requirements.requireThat(actual, "actual").isNotEqualTo(getActual, "getActual()"); + requirements.requireThat(actual, "actual").isNotEqualTo(getActual as number, "getActual()"); requirements.requireThat(getActual, "getActual").isNotDefined(); }); diff --git a/test/SetTest.mts b/test/SetTest.mts index 98b57bb..3637c15 100644 --- a/test/SetTest.mts +++ b/test/SetTest.mts @@ -1,14 +1,14 @@ -import {Requirements} from "../src/index.mjs"; +import { + Requirements, + Configuration, + TerminalEncoding +} from "../src/index.mjs"; import { suite, test } from "mocha"; import {assert} from "chai"; -import { - Configuration, - TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/SizeTest.mts b/test/SizeTest.mts index 8844db9..a9f2a02 100644 --- a/test/SizeTest.mts +++ b/test/SizeTest.mts @@ -6,9 +6,9 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration -} from "../src/internal/internal.mjs"; -import {Requirements} from "../src/index.mjs"; + Requirements +} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); diff --git a/test/StringTest.mts b/test/StringTest.mts index 40e7d6f..3a7d7e4 100644 --- a/test/StringTest.mts +++ b/test/StringTest.mts @@ -5,12 +5,12 @@ import { import {assert} from "chai"; import { Configuration, - EOS_MARKER, TerminalEncoding, - TestGlobalConfiguration, - TextOnly + TextOnly, + EOS_MARKER } from "../src/internal/internal.mjs"; -import {Requirements} from "../src/Requirements.mjs"; +import {Requirements} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; const globalConfiguration = new TestGlobalConfiguration(TerminalEncoding.NONE); const configuration = new Configuration(globalConfiguration); @@ -278,13 +278,13 @@ suite("StringTest", () => test("validateThatNullAsString", () => { const actual = null; - const expected = 5; + const expected = "not-null"; const expectedMessage = "actual.asString() must be equal to " + expected + ".\n" + "\n" + - "Actual : null" + TextOnly.DIFF_PADDING + EOS_MARKER + "\n" + - "Diff : " + TextOnly.DIFF_DELETE.repeat("null".length) + TextOnly.DIFF_INSERT + - TextOnly.DIFF_PADDING.repeat(EOS_MARKER.length) + "\n" + - "Expected: " + TextOnly.DIFF_PADDING.repeat("null".length) + "5" + EOS_MARKER; + "Actual : n" + TextOnly.DIFF_PADDING.repeat("ot-n".length) + "ull" + EOS_MARKER + "\n" + + "Diff : " + TextOnly.DIFF_EQUAL + TextOnly.DIFF_INSERT.repeat("on-n".length) + + TextOnly.DIFF_EQUAL.repeat("ull".length) + TextOnly.DIFF_PADDING.repeat(EOS_MARKER.length) + "\n" + + "Expected: not-null" + EOS_MARKER; const expectedMessages = [expectedMessage]; const actualFailures = requirements.validateThat(actual, "actual").asString().isEqualTo(expected). diff --git a/src/internal/TestGlobalConfiguration.mts b/test/TestGlobalConfiguration.mts similarity index 91% rename from src/internal/TestGlobalConfiguration.mts rename to test/TestGlobalConfiguration.mts index 7132015..3ec7312 100644 --- a/src/internal/TestGlobalConfiguration.mts +++ b/test/TestGlobalConfiguration.mts @@ -1,5 +1,5 @@ -import type {TerminalEncoding} from "./internal.mjs"; -import {AbstractGlobalConfiguration} from "./internal.mjs"; +import type {TerminalEncoding} from "../src/index.mjs"; +import {AbstractGlobalConfiguration} from "../src/internal/internal.mjs"; class TestGlobalConfiguration extends AbstractGlobalConfiguration { diff --git a/test/TypescriptCompiler.mts b/test/TypescriptCompiler.mts new file mode 100644 index 0000000..0f31985 --- /dev/null +++ b/test/TypescriptCompiler.mts @@ -0,0 +1,99 @@ +import ts, {type CompilerHost} from "typescript"; +import * as path from "path"; + +/** + * Detects whether a code snippet contains compilation errors. + */ +class TypeScriptCompiler +{ + // The root directory relative to which external files will be interpreted + private static readonly rootDir: string = path.resolve("."); + private static readonly fileToExclude = "build.mts"; + private static readonly snippetFilename = "test.mts"; + private static readonly config = TypeScriptCompiler.createParsedCommandLine(); + private static readonly defaultCompilerHost = ts.createCompilerHost(TypeScriptCompiler.config.options); + + private static createParsedCommandLine() + { + // Example of compiling using API: https://gist.github.com/jeremyben/4de4fdc40175d0f76892209e00ece98f + const configFile = ts.findConfigFile(TypeScriptCompiler.rootDir, ts.sys.fileExists, "tsconfig.json"); + if (!configFile) + throw Error("tsconfig.json not found"); + const {config} = ts.readConfigFile(configFile, ts.sys.readFile); + + // We have to include at least one existing file to avoid CompilerHost throwing an exception + config.include = [TypeScriptCompiler.snippetFilename, TypeScriptCompiler.fileToExclude]; + config.exclude.concat(["src/**", "test/**"]); + + return ts.parseJsonConfigFileContent(config, ts.sys, TypeScriptCompiler.rootDir); + } + + /** + * Compiles a code snippet. + * + * @param snippet - the code to compile + * @return the compiler warnings and errors + */ + public compile(snippet: string) + { + const compilerHost: CompilerHost = { + fileExists: ts.sys.fileExists, + readFile: fileName => + { + if (fileName === TypeScriptCompiler.snippetFilename) + return snippet; + if (fileName === TypeScriptCompiler.fileToExclude) + return undefined; + return TypeScriptCompiler.defaultCompilerHost.readFile(fileName); + }, + writeFile: () => + { + // Avoid writing anything to disk + }, + getSourceFile: (fileName, languageVersion, onError, shouldCreateNewSourceFile) => + { + if (fileName === TypeScriptCompiler.snippetFilename) + return ts.createSourceFile(fileName, snippet, languageVersion); + if (fileName === TypeScriptCompiler.fileToExclude) + return undefined; + return TypeScriptCompiler.defaultCompilerHost.getSourceFile(fileName, languageVersion, onError, + shouldCreateNewSourceFile); + }, + getCanonicalFileName: fileName => + { + if (fileName === TypeScriptCompiler.snippetFilename) + return TypeScriptCompiler.snippetFilename; + return TypeScriptCompiler.defaultCompilerHost.getCanonicalFileName(fileName); + }, + getDefaultLibFileName: (options) => + TypeScriptCompiler.defaultCompilerHost.getDefaultLibFileName(options), + getCurrentDirectory: TypeScriptCompiler.defaultCompilerHost.getCurrentDirectory, + useCaseSensitiveFileNames: TypeScriptCompiler.defaultCompilerHost.useCaseSensitiveFileNames, + getNewLine: TypeScriptCompiler.defaultCompilerHost.getNewLine + }; + + const errors = TypeScriptCompiler.config.errors; + const program = ts.createProgram({ + options: TypeScriptCompiler.config.options, + rootNames: [TypeScriptCompiler.snippetFilename], + configFileParsingDiagnostics: errors, + host: compilerHost + }); + + const {diagnostics} = program.emit(); + + const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(diagnostics, errors); + if (allDiagnostics.length) + { + const formatHost: ts.FormatDiagnosticsHost = { + getCanonicalFileName: (path) => path, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => ts.sys.newLine + }; + return ts.formatDiagnostics(allDiagnostics, formatHost); + } + return ""; + } +} + +export {TypeScriptCompiler}; \ No newline at end of file diff --git a/test/ValidationFailureTest.mts b/test/ValidationFailureTest.mts index af36b3f..dda14cb 100644 --- a/test/ValidationFailureTest.mts +++ b/test/ValidationFailureTest.mts @@ -6,10 +6,10 @@ import {assert} from "chai"; import { Configuration, TerminalEncoding, - TestGlobalConfiguration, ValidationFailure } from "../src/internal/internal.mjs"; import {Requirements} from "../src/index.mjs"; +import {TestGlobalConfiguration} from "./TestGlobalConfiguration.mjs"; suite("ValidationFailureTest", () => { diff --git a/tsconfig.json b/tsconfig.json index 2810813..e5e55d8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,8 @@ "node_modules", "target", "scripts", - "./.eslintrc.mjs" + "./.eslintrc.mjs", + "test/TestGlobalConfiguration.mts" ], "typeRoots": [ "node_modules/@types" diff --git a/wiki/Changelog.md b/wiki/Changelog.md index 33cd08a..b956adc 100644 --- a/wiki/Changelog.md +++ b/wiki/Changelog.md @@ -1,6 +1,10 @@ Minor updates involving cosmetic changes have been omitted from this list. See https://github.com/cowwoc/requirements.java/commits/master for a full list. +## Version 3.2.2 - 2023/11/23 + +* Improvement: getActual() now returns a specific type instead of `unknown`. + ## Version 3.2.1 - 2023/07/19 * Breaking changes: diff --git a/wiki/Features.md b/wiki/Features.md index a3d2d4f..0818099 100644 --- a/wiki/Features.md +++ b/wiki/Features.md @@ -72,7 +72,7 @@ requireThat(nameToAge, "nameToAge").asMap(). ## String diff -When a [String comparison](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/ObjectVerifier.html#isEqualTo) +When a [String comparison](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/ObjectVerifier.html#isEqualTo) fails, the library outputs a [diff](String_Diff.md) of the values being compared. ![colored-diff-example4.png](colored-diff-example4.png) diff --git a/wiki/String_Diff.md b/wiki/String_Diff.md index 12055e3..66852c6 100644 --- a/wiki/String_Diff.md +++ b/wiki/String_Diff.md @@ -1,4 +1,4 @@ -When a [String comparison](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/ObjectVerifier.html#isEqualTo) +When a [String comparison](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/ObjectVerifier.html#isEqualTo) fails, the library outputs a [diff](https://en.wikipedia.org/wiki/Diff) of the values being compared. Depending on the terminal capability, you will see a [Textual](Textual_Diff.md) or [Colored](Colored_Diff.md) diff. @@ -8,4 +8,4 @@ Depending on the terminal capability, you will see a [Textual](Textual_Diff.md) We disable colors if stdout is redirected. This doesn't necessarily mean that ANSI codes are not supported, but we chose to err on the side of caution. Users can override this behavior by -invoking [GlobalRequirements.withTerminalEncoding()](https://cowwoc.github.io/requirements.js/3.2.1/docs/api/module-GlobalRequirements-GlobalRequirements.html#.withTerminalEncoding). \ No newline at end of file +invoking [GlobalRequirements.withTerminalEncoding()](https://cowwoc.github.io/requirements.js/3.2.2/docs/api/module-GlobalRequirements-GlobalRequirements.html#.withTerminalEncoding). \ No newline at end of file