The Firefly Semantics Validator package provides decorators that are used to validate your typescript model class instances.
Features that minimize validation noise are:
- Progressive Validation
- Cross Property Filtered Validation
We will illustrate how these are applied with a decorated PurchaseOrder class:
import {
IfValid,
IsAfterInstant,
IsDate,
IsDefined,
IsString,
IsInt,
} from '@fireflysemantics/validator';
export class PurchaseOrder {
@IsString()
@IsDefined()
id: string;
@IsString()
@IsDefined()
sku: string;
@IsDate()
@IsDefined()
purchaseDate: Date;
@IsAfterInstant('purchaseDate')
@IsDate()
@IsDefined()
@IfValid('purchaseDate')
receiptDate: Date;
@IsInt()
@IsDefined()
quantity: number;
constructor(o: any) {
//===================================
// Initialize the sku and id properties
// with Object.assign
//===================================
Object.assign(this, o);
}
}
We want to learn how to use these features.
Approach
Progressive Validation
If the @IsDefined
decorator validation on the sku
property fails, the rest of the decorators on the sku
property will not fire.
That is what progressive validation is.
It minimizes validation noise. Here’s an example:
let valid: boolean = validateProperty(po, 'sku');
assert.equal(valid, true, 'valid should be true');
po.sku = null;
valid = validateProperty(po, 'sku');
assert.equal(valid, false, 'valid should be false');
let oe: ObjectErrors = validate(po);
let errors: ValidationError[] = oe.errors;
assert.equal(errors.length, 1, 'There should only be one error');
Cross Property Filtered Validation
The @IsAfterInstant(‘purchaseDate’)
decorator checks that the purchaseDate
comes before the receiptDate
.
However we only want to perform the check if the purchaseDate
property is valid.
That is what the @IfValid(‘purchaseDate’)
does. If the purchaseDate
property is invalid, then receiptDate
validation will not proceed, as only the @IfValid
annotation will fire.
po.sku = 'sku';
po.purchaseDate = null;
valid = validateProperty(po, 'purchaseDate');
assert.equal(valid, false, 'valid should be false');
oe = validate(po);
errors = oe.errors;
assert.equal(errors.length, 2, 'There should only be two errors');
So in the above case only two ValidationError
instances are created. One for the @IsDefined
decorator on the purchaseDate
property and one for the @IfValid
decorator on the receiptDate
property.