The runtime-data-validation
package revolves around data validation functions. There is a long list of validation decorators available, but each of those decorators in turn relies on validation functions.
The TypeScript documentation talks a lot about type guard functions. These functions inspect an object, and provide a boolean
determination whether it is of a given Type. In discussing how to develop custom validation decorators we showed a type, CustomType, and a function, isCustomType, which determines whether an object has the CustomType shape.
Type guard functions like that can be useful in data validation. But, there is a difference between determining which type an object is, versus whether the object contains valid data. The object could be of the correct type, but have insane data values which will cause the application to crash.
To take an example, lets look at one of the decorator functions:
export function IsIntRange(min: number, max: number) {
// console.log(`params.IsIntRange ${min} ${max}`);
return generateValidationDecorator(
(value) => validators.isIntRange(value, min, max),
`Value :value: not an integer between ${min} and ${max}`);
}
This implements the @IsIntRange
decorator. This takes two parameters, min
and max
, and determines whether the number is within that range. The generateValidationDecorator
function is discussed in elsewhere, and its function is to as its name implies, which is to create the actual decorator function. It does this by using the validation function which is passed as the first parameter.
export const isIntRange = (value: string | number, min: number, max: number) => {
if (typeof value === 'number') {
if (Number.isInteger(value)
&& value >= min && value <= max) {
return true;
} else return false;
}
else if (validator.isInt(value, { min: min, max: max })) {
return true;
} else return false;
};
This is the validation function for @IsIntRange
. This particular validation function has to accommodate whether its value is a number
or a string
. If it is a number
, the validation is directly handled in this function, otherwise it is handled in a function provided by the validator.js
library.
Defining validation functions
The point of bringing this up is to demonstrate that data validation is a step beyond type validation. We can easily determine between number
and string
. But, the question of whether a string
is a numerical string is a deeper question having to do with the contents of the string. Likewise, if you want to limit the number to a given range, that's an even deeper question.
In other words, data validation is type validation plus determining whether the values are legitimate.
A variable that is supposed to represent speed is going to be valid for a given range of numerical values. It's not enough to determine that the variable is a number
or a string
which is numerical. One must also determine whether the value is within the acceptable range.
Therefore the validation functions we pass to generateValidationDecorator
should not stop at a determination of the data type. They need to determine validity as well. The @IsURL
decorator determines whether a string
is a valid URL, for example.
Using validation functions
Inside the runtime-data-validation
package, the validation functions are accessed this way:
import {
...
ValidateParams, ValidateAccessor, generateValidationDecorator,
validators
} from 'runtime-data-validation';
The validators
object contains the validation functions provided by this package.
To use one is simple. For example, the validation function for @IsAscii
is called like so: validators.isAscii(value.title)
To be used in a validation decorator, the first argument of the validation function must be the value to be validated. If the validation function takes any options, those will be passed in subsequent arguments.
Validation functions must return a boolean
value where true
says that value
is valid, and false
says it is not.