A TypeScript-first library for building strongly-typed validation rules
Fixes some typos and broken formatting in the README file.
Fixes type inference for the new and improved .must
rule.
For some reason TypeScript is unable to properly infer the type of arguments passed to the .must
rule since the new functionality was added. This release changes the types under the hood to be explicit rather than aliased, which seems to fix the problem.
This release includes a few bug fixes and small improvements, but also brings in some very useful extensions for the .must
rule which make it a lot easier to define custom validation rules and associate custom error messages with them.
.scalePrecision
rule.must
rule to make it easier to configure reusable custom rulesAll development dependencies have been updated to the latest version.
.scalePrecision
.must
This is the big change in this release, which makes it a lot more convenient to define custom validation logic that can be reused across several validators.
Previously it was possible to create reusable validation logic by defining named predicate functions:
// v2.0.0 and below
// Define some custom validation logic:
export const beNumeric = (value: string) => !isNaN(Number(value));
// Then, in the constructor of a validator:
this.ruleFor('stringProperty')
.must(beNumeric);
This is great, but in order to specify a custom error message we are forced to make a call to .withMessage
as well:
// v2.0.0 and below
// Define some custom validation logic:
export const beNumeric = (value: string) => !isNaN(Number(value));
export const beNumericErrorMessage = 'Please enter a number';
// Then, in the constructor of a validator:
this.ruleFor('stringProperty')
.must(beNumeric)
.withMessage(beNumericErrorMessage);
This works, but it feels clunky. Ideally we'd have a way of wrapping up both the validation logic and the error message into a single named variable.
Well, v2.1.0 brings exactly that:
// v2.1.0 and above
// Define some custom validation logic:
export const beNumeric = {
predicate: (value: string) => !isNaN(Number(value)),
message: 'Please enter a number',
};
// Then, in the constructor of a validator:
this.ruleFor('stringProperty')
.must(beNumeric);
Not only that, but you can also compose multiple custom rules together into arrays of rules which .must
will accept:
// v2.1.0 and above
// Define some custom validation logic:
export const beNumeric = {
predicate: (value: string) => !isNaN(Number(value)),
message: 'Please enter a number',
};
export const beAnInteger = {
predicate: (value: string) => Number(value) % 1 ===0,
message: 'Please enter a whole number',
};
export const bePositive = {
predicate: (value: string) => Number(value) > 0,
message: 'Please enter a positive number',
};
export const bePositiveInteger = [beNumeric, beAnInteger, bePositive];
// Then, in the constructor of a validator:
this.ruleFor('stringProperty')
.must(bePositiveInteger);
As you can see, these additions to the API of the .must
rule open up a ton of new possibilities when it comes to defining custom validation rules. The examples here have only touched on these possibilities - see the README for more details.
This release overhauls the target type of TModel
in rules and conditions that are part of a .ruleForEach
rule chain.
Previously the parameter of type TModel
in such rule/condition definitions referred to the array property itself, but this was not of much use and went against the intuitive expectation that it would refer to the base model. As a result, the parameter of type TModel
is now the base model itself, as opposed to the array property.
More specifically, this change affects the following rules and conditions:
.must
.setValidator
.when
.unless
Note that this is a breaking change as any rules/conditions defined within a .ruleForEach
rule chain which make use of the TModel
parameter will need to be modified. See the examples below for some guidance on how to migrate existing code.
An example based on the old behaviour might look something like this:
import { Validator } from 'fluentvalidation-ts';
type ExampleModel = {
arrayProperty: Array<number>;
};
class ExampleValidator extends Validator<ExampleModel> {
constructor() {
super();
this.ruleForEach('arrayProperty')
.must((item, array) => item.displayIndex < array.length);
}
}
As you can see, the second argument to the .must
rule is the value of arrayProperty
, rather than the value of the entire model.
An example based on the new behaviour might look something like this:
import { Validator } from 'fluentvalidation-ts';
type ExampleModel = {
arrayProperty: Array<number>;
};
class ExampleValidator extends Validator<ExampleModel> {
constructor() {
super();
this.ruleForEach('arrayProperty')
.must((item, model) => item.displayIndex < model.arrayProperty.length);
}
}
As you can see, the second argument to the .must
rule is now the value of the entire model, but we can easily drill down into the array property if we need to.
Fixes an oversight in the previous commit whereby types for the setValidator
rule had not been updated in all relevant places.
Updates the validatorProducer
argument of the setValidator
rule so that it can optionally take the base model being validated.
This change allows nested validators to depend on the state of the base model, by accepting the base model as a constructor argument.
Example:
this.ruleFor('nestedProperty')
.setValidator(baseModel => new NestedValidator(baseModel));
Changes the type of ValueValidationResult
for array values, so that it is an array of validation results for the child elements, rather than an object containing validation results for the child elements keyed by their index in the array.
This change is needed to support Formik Field Arrays.
Previously:
myValidator.validate(modelWithArrayProperty);
// { arrayProperty: { 2: 'This element is invalid' } }
Now:
myValidator.validate(modelWithArrayProperty);
// { arrayProperty: [null, null, 'This element is invalid', null] }
Note: This change may break code that relies on the validation result for an array property being an object.
Use TSdx for bundling and general configuration.
Includes further tweaks to the README file, and addresses an issue where changes to the dist
folder had not been fully checked in.