This is Part 5 of a 100 part series introducing you to TypeScript if you’ve not used it before; which will give you a brush up on things you already know if you’ve used it, and maybe introduce you to some new things that you might not have come across before. In this post, I am going to demonstrate how to use interfaces to set up types so that they must have certain behaviors.
On day 4 of our journey into TypeScript, we looked at how we could use interfaces to act as data types. As I am about to show you, that’s not all that interfaces can do. In this post, I am going to demonstrate how to use interfaces to set up types so that they must have certain behaviours. For the purposes of this post, I am going to create a simple interface to control validation.
Requirements
The requirements for the validation are going to be really straightforward.
- The validation will determine whether a
string
is greater than or equal to a minimum number of characters. - The validation will determine whether a
string
is less than or equal to a maximum number of characters. - The validation will use a single method called
IsValid
to determine whether or not the string
is valid.
With these simple requirements in place, I am ready to start writing my code.
Before I start writing the code, I want to address something you might be wondering, namely, why have I written my requirements down? The answer to this is straightforward; as a professional developer, I like to know what it is that I am writing. I find that the act of writing requirements down is a great place for me to start solving the problem I am writing the code for.
The Implementation
Okay, getting back to the code. One of my requirements was that I wanted a single method called IsValid
that my validation code will use. This is where the interface
comes in; interfaces do not have any implementation capabilities so I cannot write any actual logic in my interface, I can say what methods I want to use. This is the code for the interface.
interface Validate {
IsValid(): boolean;
}
So, now I need to do something with the interface. To fulfil my requirements, I am going to create a class that validates whether or not the string
is a minimum length and another class to determine whether or not the class is a maximum length. Both of these classes will use the interface; to do this, I need to use implements
to say that the class implements the interface.
class MinimumLengthValidation implements Validate {
constructor(private text: string, private minimumLength: number) {}
IsValid(): boolean {
return this.text.length >= this.minimumLength;
}
}
You will probably notice that the constructor looks unusual. I have declared text
and minimumLength
in the signature of the constructor. By marking them as private
, I have told TypeScript that I want these assigned here. Effectively, this code is exactly the same as this:
class MinimumLengthValidation implements Validate {
private text: string;
private minimumLength: number;
constructor(text: string, minimumLength: number) {
this.text = text;
this.minimumLength = minimumLength;
}
IsValid(): boolean {
return this.text.length >= this.minimumLength;
}
}
The maximum length validation looks remarkably similar to this code unsurprisingly.
class MaximumLengthValidation implements Validate {
constructor(private text: string, private maximumLength: number) {}
IsValid(): boolean {
return this.text.length <= this.maximumLength;
}
}
Testing the Code
Having written the validation classes, I am ready to test them. I could write my code like this.
console.log(new MinimumLengthValidation('ABC12345', 10).IsValid());
console.log(new MinimumLengthValidation('ABC12345AB12345', 10).IsValid());
console.log(new MaximumLengthValidation('ABC12345', 10).IsValid());
console.log(new MaximumLengthValidation('ABC12345AB12345', 10).IsValid());
That doesn’t really demonstrate my validation, so let’s take a look at using the Validate
interface. I am going to create an array of Validate
items.
const validation: Validate[] = [];
What I am going to do now is push
the same validation items from the little snippet above into the array.
validation.push(new MinimumLengthValidation('ABC12345', 10));
validation.push(new MinimumLengthValidation('ABC12345AB12345', 10));
validation.push(new MaximumLengthValidation('ABC12345', 10));
validation.push(new MaximumLengthValidation('ABC12345AB12345', 10));
With this in place, I am going to use a loop to work my way through the array and print out whether or not the validation has succeeded.
for (let index = 0; index < validation.length; index++) {
console.log(validation[index].IsValid());
}
Code
We have reached the end of using interfaces to describe what behaviours a class can have. We are starting to move into the territory of inheritance, one of the pillars of Object-Orientation. In the next post, I am going to go further into the world of inheritance and this is where we are really going to pick up the pace.
Thank you so much for reading. As always, the code behind this article is available on Github.
History
- 1st November, 2021: Initial version