diff --git a/readme.md b/readme.md index a04a404..e25eeb0 100644 --- a/readme.md +++ b/readme.md @@ -107,6 +107,30 @@ checkPassword('foo'); //=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo` ``` +### ow.validate(predicate) + +Create a reusable validator. It returns error and value. + +```ts +const checkPassword = ow.validate(ow.string.minLength(6)); + +const password = 'foo'; + +const {error, value} = checkPassword(password); +//=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo` +``` + +### ow.createValidate(label, predicate) + +Create a reusable validator with a specific `label`. It returns error and value. + +```ts +const checkPassword = ow.createValidate('password', ow.string.minLength(6)); + +const {error, value} = checkPassword('foo'); +//=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo` +``` + ### ow.any(...predicate[]) Returns a predicate that verifies if the value matches at least one of the given predicates. diff --git a/source/index.ts b/source/index.ts index 3dd5347..280387b 100644 --- a/source/index.ts +++ b/source/index.ts @@ -52,6 +52,38 @@ export interface Ow extends Modifiers, Predicates { @param predicate - Predicate used in the validator function. */ create(label: string, predicate: BasePredicate): ReusableValidator; + + /** + Test if the value matches the predicate. It returns error and value. + + @param value - Value to test. + @param predicate - Predicate to test against. + */ + validate(value: T, predicate: BasePredicate): {error: Error | null; value: T}; + + /** + Test if the value matches the predicate. It returns error and value. + + @param value - Value to test. + @param label - Label which should be used in error messages. + @param predicate - Predicate to test against. + */ + validate(value: T, label: string, predicate: BasePredicate): {error: Error | null; value: T}; + + /** + Create a reusable validator. It returns error, It ReusableValidator returns error. + + @param predicate - Predicate to test against. + */ + createValidate(predicate: BasePredicate): (value: T, label?: string) => {error: Error | null; value: T}; + + /** + Create a reusable validator. It returns error, It ReusableValidator returns error. + + @param label - Label which should be used in error messages. + @param predicate - Predicate to test against. + */ + createValidate(label: string, predicate: BasePredicate): (value: T, label?: string) => {error: Error | null; value: T}; } /** @@ -108,6 +140,40 @@ Object.defineProperties(ow, { test(value, label ?? (labelOrPredicate as string), predicate as BasePredicate); } + }, + validate: { + value: (value: T, labelOrPredicate: unknown, predicate?: BasePredicate) => { + try { + ow(value, labelOrPredicate, predicate); + + return { + error: null, + value + }; + } catch (error) { + return { + error, + value + }; + } + } + }, + createValidate: { + value: (labelOrPredicate: BasePredicate | string | undefined, predicate?: BasePredicate) => (value: T, label?: string) => { + try { + ow(value, label ?? labelOrPredicate, predicate); + + return { + error: null, + value + }; + } catch (error) { + return { + error, + value + }; + } + } } }); diff --git a/test/test.ts b/test/test.ts index c39b2cb..d54c698 100644 --- a/test/test.ts +++ b/test/test.ts @@ -279,8 +279,37 @@ test('isValid', t => { t.false(ow.isValid(true as any, ow.any(ow.string, ow.number))); }); +test('validate', t => { + const valueCauseError = 1 as any as string; + + t.notThrows(() => { + const {error, value} = ow.validate('foo', ow.string); + t.true(error === null); + t.true(value === 'foo'); + }); + + t.notThrows(() => { + const {error, value} = ow.validate('foo', 'label', ow.string); + t.true(error === null); + t.true(value === 'foo'); + }); + + t.notThrows(() => { + const {error, value} = ow.validate(valueCauseError, ow.string); + t.true(error instanceof Error); + t.true(value === valueCauseError); + }); + + t.notThrows(() => { + const {error, value} = ow.validate(valueCauseError, 'label', ow.string); + t.true(error instanceof Error); + t.true(value === valueCauseError); + }); +}); + test('reusable validator', t => { const checkUsername = ow.create(ow.string.minLength(3)); + const checkNickname = ow.createValidate('nickname', ow.string.minLength(3)); const value = 'x'; @@ -303,6 +332,18 @@ test('reusable validator', t => { t.throws(() => { checkUsername(5 as any); }, 'Expected argument to be of type `string` but received type `number`'); + + t.notThrows(() => { + const {error, value} = checkNickname('foo'); + t.true(error === null); + t.true(value === 'foo'); + }); + + t.notThrows(() => { + const {error, value} = checkNickname('fo'); + t.true(error instanceof Error); + t.true(value === 'fo'); + }); }); test('reusable validator called with label', t => {