Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Typescript

100 Days of TypeScript (Day 3)

5.00/5 (3 votes)
28 Oct 2021CPOL6 min read 5.1K  
More in-depth look at classes, how to add our own constructors and change whether or not code outside our class can see our fields
This is Part 3 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 part, you will see classes in more depth, learn how to add our own constructors and change whether or not code outside our class can see our fields.

Welcome back to a series about learning TypeScript from basics through to some pretty advanced stuff. On Day 2, we learned how we can create a simple class using TypeScript. Today, we are going to explore classes in more depth, showing how we can add our own constructors and how we can change whether or not code outside our class can see our fields.

Requirements

I have to admit that I’m a bit bored with writing a simple addition class so I am going to write a class that has a little bit more to it. Today, I am going to write a Point class. As a professional developer, I like to have an idea about what I want my class to do so I am going to give it the following requirements.

  1. The class will allow me to add an x and a y value to represent a single point.
  2. The class will provide me with an IsEmpty method so I can determine whether or not the x and y numbers aren’t set to 0,0.
  3. The only way to change the x and y value is through an Offset method.
  4. I will be able to add a Point to a point to get a new point.
  5. I can determine whether two points are equal (have the same coordinates) through an IsEqual method
  6. I will have a ToString method which will tell people using the class what the x and y values are.

The code for today’s exercise is available here. If you look at the code, you will see that I have created a tsconfig.json file. This is exactly the same file I created for Day 2 and Day 1 so you could copy the one you created previously or add a new one using:

tsc --init

Getting Started

In my day3.ts class, I am going to start by creating the basic class structure.

TypeScript
class Point {
}

A Quick Sidenote

You might wonder why you haven’t seen anything called object, but I keep talking about object-oriented programming. I have seen many complicated explanations but there is a really simple explanation. When we talk about an object, we are talking about something that has been created by the application while it is running; a class is the template for the object so when we create a new instance of our class (instantiate the class is another term you might see), we have actually created an object. We can write a program with thousands of classes; they aren’t useful until we actually create instances from them.

The Constructor

When we create an instance of any classes we are writing, you can think of it as we are constructing the class. To help us construct the instance, we have a special method called a constructor. This method is especially interesting because it helps us to get the instance into a start that we can use it, so it can have a large effect on what happens while the class is being instantiated. As this method is used to construct the instance, you can’t call it directly from TypeScript. The only thing that has direct interaction with it is the new keyword. So, what does a constructor look like?

In TypeScript, a constructor uses the constructor keyword like this:

TypeScript
constructor() {
}

Hint: If your constructor looks like this, you can remove it. If you don’t add a constructor to your class, one is automatically “added” for you that looks like this. You won’t see it in your code, but it is there. This is the reason I didn’t add a constructor to my code on Day 2.

In my requirements, I said that the class would allow me to add x and y values to represent the point. To do this, I am going to pass these values into the constructor like this:

TypeScript
constructor(x: number, y: number) {
}

With this in place, anywhere that I needed to create an instance of this class, I can create it like this:

TypeScript
class Point {
    constructor(x: number, y: number) {
    }
}
const point: Point = new Point(0, 0);

Note: As I have added a constructor with parameters, I no longer have access to the default constructor, so I am forced to use the constructor with parameters here.

I have passed values in, but I am not actually doing anything with them. They will not be available to any method that needs them because they are visible only to the constructor. I am going to fix this by adding a couple of fields to store these values. One of my requirements is that the only way to change the value of the fields is to use an Offset method so this suggests that I should not be able to access them directly from outside the class. To do this, I am going to introduce the private keyword. What private does is tell the compiler that this field will only be visible inside the class.

You are probably thinking that we cannot make everything private and that would be correct. By default, TypeScript makes things inside the class public but you can also explicitly set things to public.

Our private fields are going to look like this:

TypeScript
private x: number;
private y: number;

If you remember, on Day 2, I had to assign a default value to our fields when I created them. When I assign values to them in the constructor, the compiler is clever enough to know that I don’t have to assign default values. My constructor now looks like this:

TypeScript
constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
}

Upcoming. In a future post, I will show you a handy trick of TypeScript where I can declare a field in the signature of the constructor.

I am now going to address my other requirements. Let us start with the ability to check whether or not a point is empty. I have taken the decision here that an empty point is one where the x and y values are both set to 0.

TypeScript
public IsEmpty(): boolean {
    return this.x === 0 && this.y === 0;
}

The next method I am going to write checks whether two points are equal. Equality, in this case, is whether the x values in both points are the same and the y values are the same. As this code will run in an instance of the class, I only need to pass in the Point that I want to compare against.

TypeScript
public IsEqual(point: Point): boolean {
    return this.x === point.x && this.y === point.y;
}

With little increments of code like this, I can quickly add all the functionality that I need. The ability to offset my coordinates looks like this:

TypeScript
public Offset(x: number, y: number) {
    this.x = this.x + x;
    this.y = this.y + y;
}

Important note: There is a shorthand way to add another value to an existing one. If I use += in my code, I can change

TypeScript
this.x = this.x + x;

to

TypeScript
this.x += x;

Rather than going through the other methods, I will add the whole class here so you can see what the ToString and Add methods look like. By now, you should have a good idea about what these methods would probably do.

TypeScript
class Point {
    private x: number;
    private y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    public IsEmpty(): boolean {
        return this.x === 0 && this.y === 0;
    }

    public IsEqual(point: Point): boolean {
        return this.x === point.x && this.y === point.y;
    }

    public Add(point: Point): Point {
        return new Point(point.x + this.x, point.y + this.y);
    }

    public ToString(): string {
        return 'X is ' + this.x + ' Y is ' + this.y;
    }

    public Offset(x: number, y: number): void {
        this.x += x;
        this.y += y;
    }
}

If I want to demonstrate this code in operation, I can write something like this:

TypeScript
const point: Point = new Point(0, 0);
console.log('Point is empty is ' + point.IsEmpty()); // Should be true
point.Offset(10, 20);
console.log('Point is empty is ' + point.IsEmpty()); // Should be false
const offsetPoint = new Point(10, 20);
console.log('Points are equal is ' + point.IsEqual(offsetPoint)); // Should be true
console.log('The offset is ' + point.Add(offsetPoint).ToString()); // X is 20 and Y is 40

We have reached the end of the code for Day 3. I appreciate that there is a lot to take in here, from creating a custom constructor, adding scope modifiers (public/private) and a little bit of alternate addition syntax. Please browse the code from Github and, if you have any questions, don’t be afraid to ask. On day 4, I am going to introduce you to interfaces in TypeScript. If you have used interfaces in a language like C# or Java, the way that TypeScript lets you use them looks nothing short of miraculous.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)