Skip to content

Commit

Permalink
Avoid unnecessary inference of stack for label (#213)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
hanshsieh and sindresorhus authored Jun 26, 2021
1 parent 49841a2 commit 6075739
Show file tree
Hide file tree
Showing 8 changed files with 25 additions and 16 deletions.
2 changes: 1 addition & 1 deletion source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import test from './test';
/**
@hidden
*/
export type Main = <T>(value: T, label: string | Function, predicate: BasePredicate<T>) => void;
export type Main = <T>(value: T, label: string | Function, predicate: BasePredicate<T>, idLabel?: boolean) => void;

// Extends is only necessary for the generated documentation to be cleaner. The loaders below infer the correct type.
export interface Ow extends Modifiers, Predicates {
Expand Down
4 changes: 2 additions & 2 deletions source/predicates/any.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export class AnyPredicate<T = unknown> implements BasePredicate<T> {
private readonly options: PredicateOptions = {}
) {}

[testSymbol](value: T, main: Main, label: string | Function): asserts value {
[testSymbol](value: T, main: Main, label: string | Function, idLabel: boolean): asserts value {
const errors = new Map<string, Set<string>>();

for (const predicate of this.predicates) {
try {
main(value, label, predicate);
main(value, label, predicate, idLabel);
return;
} catch (error: unknown) {
if (value === undefined && this.options.optional === true) {
Expand Down
2 changes: 1 addition & 1 deletion source/predicates/base-predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ export const isPredicate = (value: unknown): value is BasePredicate => Boolean((
@hidden
*/
export interface BasePredicate<T = unknown> {
[testSymbol](value: T, main: Main, label: string | Function): void;
[testSymbol](value: T, main: Main, label: string | Function, idLabel?: boolean): void;
}
2 changes: 1 addition & 1 deletion source/predicates/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ObjectPredicate<T extends object = object> extends Predicate<T> {
valuesOfType<T>(predicate: BasePredicate<T>): this {
return this.addValidator({
message: (_, label, error) => `(${label}) ${error}`,
validator: object => ofType(Object.values(object), predicate)
validator: object => ofType(Object.values(object), predicate, 'values')
});
}

Expand Down
7 changes: 4 additions & 3 deletions source/predicates/predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class Predicate<T = unknown> implements BasePredicate<T> {
/**
@hidden
*/
[testSymbol](value: T, main: Main, label: string | Function): asserts value is T {
[testSymbol](value: T, main: Main, label: string | Function, idLabel: boolean): asserts value is T {
// Create a map of labels -> received errors.
const errors = new Map<string, Set<string>>();

Expand All @@ -119,9 +119,10 @@ export class Predicate<T = unknown> implements BasePredicate<T> {
}

const label2 = is.function_(label) ? label() : label;
const labelWithTick = (label2 && idLabel) ? `\`${label2}\`` : label2;

const label_ = label2 ?
`${this.type} \`${label2}\`` :
const label_ = labelWithTick ?
`${this.type} ${labelWithTick}` :
this.type;

const mapKey = label2 || this.type;
Expand Down
5 changes: 3 additions & 2 deletions source/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Validate the value against the provided predicate.
@param value - Value to test.
@param label - Label which should be used in error messages.
@param predicate - Predicate to test to value against.
@param idLabel - If true, the label is a variable or type. Default: true.
*/
export default function test<T>(value: T, label: string | Function, predicate: BasePredicate<T>): void {
predicate[testSymbol](value, test, label);
export default function test<T>(value: T, label: string | Function, predicate: BasePredicate<T>, idLabel = true): void {
predicate[testSymbol](value, test, label, idLabel);
}
11 changes: 9 additions & 2 deletions source/utils/of-type.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import ow from '..';
import test from '../test';
import {BasePredicate} from '../predicates/base-predicate';

// TODO: After we migrate all usages of this function to specify the optional 'name' parameter, we can change the parameter to be required.
/**
Test all the values in the collection against a provided predicate.
@hidden
@param source Source collection to test.
@param predicate Predicate to test every item in the source collection against.
@param name The name to call the collection of values, such as `values` or `keys`. If it is `undefined`, it uses the call stack to infer the label.
*/
export default <T>(source: IterableIterator<T> | Set<T> | T[], predicate: BasePredicate<T>): boolean | string => {
export default <T>(source: IterableIterator<T> | Set<T> | T[], predicate: BasePredicate<T>, name?: string): boolean | string => {
try {
for (const item of source) {
ow(item, predicate);
if (name) {
test(item, name, predicate, false);
} else {
ow(item, predicate);
}
}

return true;
Expand Down
8 changes: 4 additions & 4 deletions test/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,19 @@ test('object.valuesOfType', t => {

t.throws(() => {
ow({unicorn: '🦄', rainbow: 2}, ow.object.valuesOfType(ow.string));
}, '(object) Expected argument to be of type `string` but received type `number`');
}, '(object) Expected values to be of type `string` but received type `number`');

t.throws(() => {
ow({unicorn: '🦄', rainbow: 2}, 'foo', ow.object.valuesOfType(ow.string));
}, '(object `foo`) Expected argument to be of type `string` but received type `number`');
}, '(object `foo`) Expected values to be of type `string` but received type `number`');

t.throws(() => {
ow({unicorn: 'a', rainbow: 'b'}, ow.object.valuesOfType(ow.string.minLength(2)));
}, '(object `ow.number`) Expected string to have a minimum length of `2`, got `a`');
}, '(object `ow.number`) Expected string values to have a minimum length of `2`, got `a`');

t.throws(() => {
ow(['🦄', true, 1], ow.object.valuesOfType(ow.any(ow.string, ow.boolean)));
}, '(object) Any predicate failed with the following errors:\n - Expected argument to be of type `string` but received type `number`\n - Expected argument to be of type `boolean` but received type `number`');
}, '(object) Any predicate failed with the following errors:\n - Expected values to be of type `string` but received type `number`\n - Expected values to be of type `boolean` but received type `number`');
});

test('object.valuesOfTypeDeep', t => {
Expand Down

0 comments on commit 6075739

Please sign in to comment.