Browse Source

Only infer label when predicate fails (#119)

Fixes #115
string-allowed-chars
Sam Verschueren 6 years ago
committed by Sindre Sorhus
parent
commit
67a06cba29
  1. 40
      source/index.ts
  2. 6
      source/lib/predicates/any.ts
  3. 4
      source/lib/predicates/base-predicate.ts
  4. 26
      source/lib/predicates/predicate.ts

40
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 = <T>(value: T, label: string | Function, predicate: BasePredicate<T>) => 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 = <T>(value: T, labelOrPredicate: BasePredicate<T> | string | undefined, predicate?: BasePredicate<T>) => {
let label: any = labelOrPredicate;
let testPredicate: any = predicate;
const test = <T>(value: T, label: string | Function, predicate: BasePredicate<T>) => {
predicate[testSymbol](value, test, label);
};
const ow = <T>(value: T, labelOrPredicate: any, predicate?: BasePredicate<T>) => {
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<T>);
};
Object.defineProperties(main, {
Object.defineProperties(ow, {
isValid: {
value: <T>(value: T, predicate: BasePredicate<T>) => {
try {
main(value, predicate);
ow(value, predicate);
return true;
} catch {
return false;
@ -227,10 +241,14 @@ Object.defineProperties(main, {
create: {
value: <T>(labelOrPredicate: BasePredicate<T> | string | undefined, predicate?: BasePredicate<T>) => (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<T>);
}
},
any: {
@ -337,7 +355,7 @@ Object.defineProperties(main, {
}
});
export default main as Ow;
export default ow as Ow;
export {
BasePredicate,
Predicate,

6
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<T = any> implements BasePredicate<T> {
) {}
// 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) {

4
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<T = any> {
// tslint:disable-next-line completed-docs
[testSymbol](value: T, main: Ow, label?: string): void;
[testSymbol](value: T, main: Main, label: string | Function): void;
}

26
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<T = any> implements BasePredicate<T> {
* @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);
}
}

Loading…
Cancel
Save