Tic Tac Toe with TypeScript - Part 1

bornasepic

Borna Šepić

Posted on January 23, 2020

Tic Tac Toe with TypeScript - Part 1

Why TypeScript?

If you're like me and Javascript is the only programing language you've ever learned you might be a bit repulsed to get into Typescript, at the end of the day your apps work just fine, why would you need to add another layer of complexity to it?

Well, the short answer is... It makes you a better developer.
It can also drastically reduce the number of run time bugs you encounter and make the developer experience far better and more efficient (once you get into it).

As always there is a bit of a learning curve to it, and it can really be frustrating sometimes to have your trusted IDE scream at you on every save. But it's a worthwhile tradeoff in the long run.

So without further ado, let's convert a small app from regular Javascript into its typed superset that is Typescript 🚀


The setup

For our application, we'll use the Tic Tac Toe we've written in the last article.

If you don't have it already you can grab it from Github here.

First things first, we'll need to install Typescript.
You'll first want to position your terminal at the root of the project and run npm init -y. This will create our package.json file (without asking too many questions 😇) and allow us to install typescript via NPM.

Next up we'll run npm i typescript to actually install Typescript and all it needs.

I'd recommend moving our project files (index.html, styles.css, script.js) into a new folder, just to keep things nice and clean, I've named the folder src but that's totally up to you.

This is how the project should look like at this point:
Alt Text

You'll also want to run tsc --init. This will generate our tsconfig.json file to allow us to have more control over the TS compiler.

Before continuing you'll want to change the // "lib": [], line in the config file (line 7) and replace it with "lib": ["es6", "dom", "es2017"],. This will allow us to use some more advanced features of JavaScript in our code.

To actually get started all we need to do is change our script.js into script.ts. And run tsc script.ts (this will compile our TypeScript file into good old regular JavaScript).
You've probably gotten an error compiling your script.ts file, but that's expected.

Please note we are still only including the script.js file in our index.html. Since "TypeScript is a typed superset of JavaScript", your browser will never actually run TypeScript. So it a nutshell your users won't notice in any way whether your app is written in TypeScript or not (except for the lack of bugs, and a 😃 on your face).


Actual TypeScript

Now let's get to the fun part and write ourselves some TypeScript! We'll go through the script line by line and convert what we can to TypeScript.
To keep things nice and "short", for this article we'll just go through the initial variables and finish the app in another one.

In the previous tutorial, we've created some variables that are storing our game state. Let's first take a look at them.

const statusDisplay = document.querySelector('.game--status');

let gameActive = true;
let currentPlayer = "X";
let gameState = ["", "", "", "", "", "", "", "", ""];

const winningMessage = () => `Player ${currentPlayer} has won!`;
const drawMessage = () => `Game ended in a draw!`;
const currentPlayerTurn = () => `It's ${currentPlayer}'s turn`;

We first have a document.querySelector method that returns an element with the class of 'game--status'. By doing a quick search on MDN we can see that the .querySelector returns an Element.
So we'll add a type to our statusDisplay variable to let TS know it should contain and Elemenet, like this:

const statusDisplay: Element = document.querySelector('.game--status');

You should be getting an error warning here saying type 'Element | null' is not assignable to type 'Element'.

When you think about it this error makes sense, we have no guarantee that the element with a class of "game--status" exists in our DOM. If this was a bigger app we might want to handle this case just to future proof our code but since it's a small application and we know that that element will always be there and we can tell TS that it will never return null by adding an exclamation point to the end, like this:

const statusDisplay: Element = document.querySelector('.game--status')!;

Next up we have our gameActive variable. Since we know this will only contain a boolean value (either true or false) we can assign the type of boolean to our variable.

let gameActive: boolean = true;

After that we have the currentPlayer variable. This technically does only contain a string, and there would be nothing wrong with just writing something like:

let currentPlayer: string = "X";

But because we have only two distinct cases here (the variable can only be "X" or "O", we can use a more appropriate functionality of TypeScript here called Enum. So the end product should look something like this:

enum PlayerSigns {
    X = "X",
    O = "O"
}
let currentPlayer: PlayerSigns = PlayerSigns.X;

We have created an Enum that will hold our player signs, and assigned the value of our currentPlayer variable to that enum.

After that we have our gameState variable, where... we hold our game state (😎).

let gameState = ["", "", "", "", "", "", "", "", ""];

We can see that this will always be an array of strings, so we can pass that on to our comipler like this:

let gameState: string[] = ["", "", "", "", "", "", "", "", ""];

And lastly, we have our three functions that return our game status messages:

const winningMessage = () => `Player ${currentPlayer} has won!`;
const drawMessage = () => `Game ended in a draw!`;
const currentPlayerTurn = () => `It's ${currentPlayer}'s turn`;

Since they are all simple functions, without any inputs, that return strings we can use the same types for all of them.

const winningMessage: () => string = () => `Player ${currentPlayer} has won!`;
const drawMessage: () => string = () => `Game ended in a draw!`;
const currentPlayerTurn: () => string = () => `It's ${currentPlayer}'s turn`;

It can seem a bit annoying at times to have to write all the types yourself, but it's another one of those things that become second nature after a brief adjustment period.

Hopefully, by the end of this series, you'll be convinced on the benefits of using TypeScript for your project.

As always, thanks for reading, and until the next one ✌️

💖 💪 🙅 🚩
bornasepic
Borna Šepić

Posted on January 23, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Application State Management
typescript Application State Management

March 11, 2021

Tic Tac Toe with TypeScript - Part 1
typescript Tic Tac Toe with TypeScript - Part 1

January 23, 2020

Migrating to TypeScript
typescript Migrating to TypeScript

October 27, 2019