diff --git a/source/index.ts b/source/index.ts index 024b4c0..0d86e1f 100644 --- a/source/index.ts +++ b/source/index.ts @@ -17,6 +17,11 @@ import {WeakSetPredicate} from './lib/predicates/weak-set'; type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; +/** + * @hidden + */ +export type Main = (value: T, label: string | Function, predicate: BasePredicate) => void; + export interface Ow { /** * Test if the value matches the predicate. Throws an `ArgumentError` if the test fails. @@ -201,23 +206,32 @@ export interface Ow { any(...predicate: BasePredicate[]): AnyPredicate; } -const main = (value: T, labelOrPredicate: BasePredicate | string | undefined, predicate?: BasePredicate) => { - let label: any = labelOrPredicate; - let testPredicate: any = predicate; +const test = (value: T, label: string | Function, predicate: BasePredicate) => { + predicate[testSymbol](value, test, label); +}; + +const ow = (value: T, labelOrPredicate: any, predicate?: BasePredicate) => { + if (!isPredicate(labelOrPredicate) && typeof labelOrPredicate !== 'string') { + throw new TypeError(`Expected second argument to be a predicate or a string, got \`${typeof labelOrPredicate}\``); + } if (isPredicate(labelOrPredicate)) { - label = inferLabel(callsites()); - testPredicate = labelOrPredicate; + // If the second argument is a predicate, infer the label + const stackFrames = callsites(); + + test(value, () => inferLabel(stackFrames), labelOrPredicate); + + return; } - return testPredicate[testSymbol](value, main, label); + test(value, labelOrPredicate, predicate as BasePredicate); }; -Object.defineProperties(main, { +Object.defineProperties(ow, { isValid: { value: (value: T, predicate: BasePredicate) => { try { - main(value, predicate); + ow(value, predicate); return true; } catch { return false; @@ -227,10 +241,14 @@ Object.defineProperties(main, { create: { value: (labelOrPredicate: BasePredicate | string | undefined, predicate?: BasePredicate) => (value: T) => { if (isPredicate(labelOrPredicate)) { - return main(value, inferLabel(callsites()), labelOrPredicate); + const stackFrames = callsites(); + + test(value, () => inferLabel(stackFrames), labelOrPredicate); + + return; } - return main(value, labelOrPredicate, predicate); + test(value, labelOrPredicate as string, predicate as BasePredicate); } }, any: { @@ -337,7 +355,7 @@ Object.defineProperties(main, { } }); -export default main as Ow; +export default ow as Ow; export { BasePredicate, Predicate, diff --git a/source/lib/predicates/any.ts b/source/lib/predicates/any.ts index 4d41e66..7aabc1c 100644 --- a/source/lib/predicates/any.ts +++ b/source/lib/predicates/any.ts @@ -1,6 +1,6 @@ import {ArgumentError} from '../argument-error'; import {BasePredicate, testSymbol} from './base-predicate'; -import {Ow} from '../..'; +import {Main} from '../..'; /** * @hidden @@ -11,14 +11,14 @@ export class AnyPredicate implements BasePredicate { ) {} // tslint:disable completed-docs - [testSymbol](value: T, main: Ow, label?: string) { + [testSymbol](value: T, main: Main, label: string | Function) { const errors = [ 'Any predicate failed with the following errors:' ]; for (const predicate of this.predicates) { try { - main(value, label as string, predicate); + main(value, label, predicate); return; } catch (err) { diff --git a/source/lib/predicates/base-predicate.ts b/source/lib/predicates/base-predicate.ts index 0f4cede..c9be36e 100644 --- a/source/lib/predicates/base-predicate.ts +++ b/source/lib/predicates/base-predicate.ts @@ -1,4 +1,4 @@ -import {Ow} from '../..'; +import {Main} from '../..'; /** * @hidden @@ -15,5 +15,5 @@ export const isPredicate = (value: any): value is BasePredicate => Boolean(value */ export interface BasePredicate { // tslint:disable-next-line completed-docs - [testSymbol](value: T, main: Ow, label?: string): void; + [testSymbol](value: T, main: Main, label: string | Function): void; } diff --git a/source/lib/predicates/predicate.ts b/source/lib/predicates/predicate.ts index 2d1384b..38adb2e 100644 --- a/source/lib/predicates/predicate.ts +++ b/source/lib/predicates/predicate.ts @@ -1,5 +1,5 @@ import is from '@sindresorhus/is'; -import {Ow} from '../..'; +import {Main} from '../..'; import {ArgumentError} from '../argument-error'; import {BasePredicate, testSymbol} from './base-predicate'; import {not} from '../operators/not'; @@ -55,18 +55,26 @@ export class Predicate implements BasePredicate { * @hidden */ // tslint:disable completed-docs - [testSymbol](value: T, main: Ow, label?: string) { - const label2 = label - ? `${this.type} \`${label}\`` - : this.type; - + [testSymbol](value: T, main: Main, label: string | Function) { for (const {validator, message} of this.context.validators) { const result = validator(value); - if (typeof result !== 'boolean' || !result) { - // TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement - throw new ArgumentError(message(value, label2, result), main); + if (result === true) { + continue; + } + + let label2 = label; + + if (typeof label === 'function') { + label2 = label(); } + + label2 = label2 + ? `${this.type} \`${label2}\`` + : this.type; + + // TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement + throw new ArgumentError(message(value, label2, result), main); } }