Advanced TypeScript Programming Projects
上QQ阅读APP看书,第一时间看更新

Validating user inputs and the use of validators

At this stage, we really should think about validating inputs from the user. We are going to introduce two types of validation in our code. The first is the minimum length validation. In other words, we are going to ensure that some of the entries have to have a minimum number of entries before they can be considered to be valid. The second type of validation uses something called a regular expression to validate it. What this means is that it takes the input and compares it against a set of rules to see whether there is a match; the expressions can look a little bit odd if you are new to regular expressions, so we will break them down to see exactly what rules we are applying.

We are going to break our validation down into three parts:

  1. The classes that provide the checking features, such as applying a regular expression. We will call these validators.
  2. The classes that apply the validation items to the different parts of the state. We will call these classes validations.
  3. The component that will call the validation items and update the UI with the details of a failed validation. This will be a new component called FormValidation.tsx.

We will start by creating an interface called IValidator. This interface is going to accept a generic parameter so that we can apply it to pretty much anything that we want. As a validation will tell us whether the input is valid, it will have a single method called IsValid that accepts the relevant input and then returns a boolean value:

interface IValidator<T> {
IsValid(input : T) : boolean;
}

The first validator that we are going to write checks to see whether a string has a minimum number of characters, which we will set through the constructor. We will also guard against situations where the user fails to supply an input, by returning false from IsValid when the input is null:

export class MinLengthValidator implements IValidator<StringOrNull> {
private minLength : number;
constructor(minLength : number) {
this.minLength = minLength;
}
public IsValid(input : StringOrNull) : boolean {
if (!input) {
return false;
}
return input.length >= this.minLength;
}
}

The other validator that we are going to create is slightly more complicated. This validator accepts a string, which it uses to create something called a regular expression. A regular expression is effectively a mini language that provides a set of rules to test our input string against. In this case, the rules that form our regular expression are passed into our constructor. The constructor will then instantiate an instance of the JavaScript regular expression engine (RegExp). In a similar way to the minimum length validation, we ensure that we return false if there is no input. If we have an input, then we return the result of our regular expression test:

import { StringOrNull } from 'src/Types';

export class RegularExpressionValidator implements IValidator<StringOrNull> {
private regex : RegExp;
constructor(expression : string) {
this.regex = new RegExp(expression);
}
public IsValid (input : StringOrNull) : boolean {
if (!input) {
return false;
}
return this.regex.test(input);
}
}

Now that we have our validators, we are going to examine how we are going to apply them. It probably will not come as a surprise that the first thing that we are going to do is define an interface that forms the contract of what we want our validation to do. Our Validate method is going to accept the IPersonState state from our component, validate items from this, and then return an array of validation failures:

export interface IValidation {
Validate(state : IPersonState, errors : string[]) : void;
}

I have decided to break the validation down into the following three areas:

  1. Validating the address
  2. Validating the name
  3. Validating the phone number