Typescript Classes and Interfaces

A few of the HUGE advantages TypeScript has over JavaScript for game development are interfaces and classes. Interfaces are very useful tools in your OOP toolkit. If your not familiar with interfaces from other programming languages, they are a way for a set of classes have similar signitures for methods and attributes. Classes of course are the mainstay of Object Oriented Programming, and they are a tool that is very much lacking in traditional JavaScript development. When you're developing games, it's a very useful thing to encapsulate your code inside of classes allowing you to deal with your code at a higher conceptual level. In this part of the tutorial, I'm going to show you how to use interfaces and classes to create objects that can be displayed on the canvas.

TypeScript Interfaces

The first thing we're going to do is create a shape interface. An interface for shapes will be a very useful thing because it will allow us to create a common interface for moving and drawing our shapes without actually knowing what shape it is we are drawing.

This is what our shape interface is going to look like:

interface iShape {
   draw(): void;
   x: number;
   y: number;
   color: string;
   lineWidth: number;
}

The method that will be common to all shapes is the draw() method. This is the method that will change in it's implementation for every class that implements it, a circle is going to be drawn differently than a rectangle. The "x" and "y" attributes indicate the position where the shape will be drawn. The attribute "color" will be the color that the shape will be drawn. "lineWidth" will be the width of the line in pixels to draw the shape.

TypeScript Circle Class

The first class I'm going to put together is a Circle class that implements our shape interface (iShape). I like to name interfaces with a beginning "i" for interface and classes with a beginning "c" for class.

class cCircle implements iShape {
   public x: number = 0;
   public y: number = 0;
   public radius: number = 10;
   public lineWidth: number = 2;
   public color: string = "red";
   constructor(x: number, y: number, radius: number, color: string = "red", line_width: number = 2)
   {
      this.x = x;
      this.y = y;
      this.radius = radius;
      this.color = color;
      this.lineWidth = line_width;
   }
   public draw = (): void => {
      ctx.save();
      ctx.beginPath();
      ctx.strokeStyle = this.color;
      ctx.lineWidth = this.lineWidth;
      ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
      ctx.stroke();
      ctx.restore();
   }
}

In order to implement the iShape interface, the cCircle class must have all the attributes and methods listed in the iShape interface. The attributes for cCircle must include, x, y, lineWidth, and color, and the methods must include a "draw()" method. The cCircle class also has a constructor, which get's called whenever you create a new cCircle. Also, make sure you add your class definition for cCircle before your game loop. We're going to be adding calls to "new cCircle" in front of our game loop below, and you need to have the class defined before that.

The TypeScript fat arrow

If you look at the draw method in the cCircle class, you may be wondering what "=>" means. That little operator is called the fat arrow, and when it's used in a method it solves a problem that you can have using the "this" operator in JavaScript or TypeScript. So if you're familiar with using "this" in just about any object oriented language, it refers to the object that you are currently using that method on. However, in JavaScript that is not always the case. For some reason "this" ends up refering to weird things like the document... I don't have any idea why JavaScript does things this way, but it's a weird thing about that language. Unfortunately, TypeScript kind of inherits that "feature" from JavaScript, unless, you use the fat arrow (=>) to define the method. Using the fat arrow makes "this" in your method work the way it should. I think it's kind of a strange work around, but it's better than the "self = this" nonsense you have to do in JavaScript.

Using a class to render a circle

So once we've added the iShape interface and the cCircle class to the game, we're ready to create a few circles and then draw them from within the game loop. You can create the circles above your game loop using the "new cCircle" call. If you notice, in the constructor for the cCircle class I have parameters 4 and 5 with default values. I default the circle to be red with a line width of 2. The object circle1 uses those default values because it only passes in 3 parameters. When we create circle2, we override those default values by setting the color to "blue" and the line width to 5. Inside the game loop, we then call the "draw" method on both of those circle objects.

var circle1: cCircle = new cCircle(200, 300, 50);
var circle2: cCircle = new cCircle(400, 550, 150, "blue", 5);


function gameLoop() {
   requestAnimationFrame(gameLoop);
   ctx.fillStyle = "black";
   ctx.fillRect(0, 0, 1280, 720);
   circle1.draw();
   circle2.draw();
}

Running your TypeScript App

If you're using Visual Studio Community at this point it is really easy to run your app. My default browser happens to be Google Chrome. Visual Studio has a little "Play" button at the top of the screen labeled "Google Chrome". If I click this, Visual Studio compiles (transpiles) my TypeScrpit code into JavaScript, runs a little webserver locally within Visual Studio, and loads my app into Google Chrome... Neat :-)

When you run this app, all you should be getting at this point is two circles that don't really do anything.

Check out the full code we wrote to get this working: Draw Circle Code

Let's take things a step further in the next part of our tutorial and try to actually get these circles moving.

Part 3 - Moving TypeScript Canvas Objects

Part 1 - TypeScript HTML5 Canvas Basics