Super Javascript
Home About RSS Guest Blogging 🐍 Snake

Make fewer JS mistakes with TypeScript

Make fewer JS mistakes with TypeScript

Javascript (JS) was originally intended to make small alterations to web pages, such as spinning animations when you scroll over an email @ symbol, or to validate that you have typed an email address correctly. In the years since, JS has been used for more complex things. For example it is used for so called “Single Page Applications”, which are like traditional desktop applications delivered on the web browser. The first famous application like this was Google’s Gmail.

In 2011 NodeJS was released, letting developers write JS code to run on servers to provide back-ends for websites. These back-ends are required to create dynamic websites that allow people to log in, store information and communicate. Traditionally these would be written in languages like Java, C#, Ruby or Python, but NodeJS became popular, in part because you can use the same programming language of JS for the web page as you do on the back end.

In 2013 GitHub released Electron, a tool-kit for creating traditional desktop applications using … you guessed it … Javascript. Electron is based on web technologies, so the HTML, CSS and Javascript you write for the web can be reused in Electron. However Electron gives developers access to resources on the user’s computer, such as local files, camera and sound.

With Javascript usage evolving from simple website tweaks to a language that is being used everywhere, it has shown signs of strain keeping up. Different versions of Javascript have been released such as ES5 in 2009 and ES6 in 2015 to modernise the language. However all versions of Javascript suffer from a problem that makes it hard to manage larger programs. This is a problem that is solved by TypeScript.

Javascript’s Problem

The problem with Javascript is that is uses “dynamic typing”.

What does that mean? It means that when you write the code, you have the flexibility to use objects, arrays etc. in any way you see fit, and there will be no errors at the time you write the code or compile it.

That sounds like a great thing right?

Well the catch is you will still get errors when you run the program. By then it is ‘too late’. To make things worse some of those errors will only be uncovered in rare situations. Situations that users of your program might find themselves in, but that you didn’t test for.

To illustrate the problem, imagine you have a variable passed in to a function called honestly_im_a_string. Then that variable could be a string (and probably should be!), but it could be anything at all, including:

If you just assume the value is a string and start doing things like find/replace with it, then the program will error if another piece of code passes an array into it.

Is this a likely mistake. Yes! Perhaps someone else is writing the code that calls your function and they didn’t know. It could be the same programmer revisiting code from months ago making this mistake. With lots of functions and variables to keep track of, this sort of mistake could even happen if the same programmer writes both the function and the code that uses it on the same day!

As programs get larger and there are more functions and more objects, it can be easy to lose track of what these functions expect. For example if you pass in an object with coordinates, is it x and y or X and Y that are expected as properties? Is it expecting an array of these or just one. It is easy to make a mistake somewhere, and often only find out when it is too late and the program is being used by users who call you up with a problem.

The solution

The solution is “static typing”.

This is where you define the type of variables when you write the program. Before you run your program a compiler will check that you haven’t misused any types.

In the honestly_im_a_string example, this means you would be told before you run your program that there is an error, because someone is trying to pass an array into a function that wants a string.

In the coordinate example, you’d define that your object has an x and if you try to get it’s X you will get an error from the compiler, so you can fix it before you even run the program for the first time.

TypeScript provides the ability to define the type of variables in your program. This works by writing your program in the TypeScript language, which is like Javascript but it allows you to “annotate” your variables with types.

TypeScript programs are compiled into Javascript by a tool called “tsc” or “TypeScript Compiler”. During this process, it will check that you haven’t made any mistakes.

As an example, less look at a TypeScript function that takes a string and adds a full stop (or period) to the end:

function addPeriod(sentence: string) {
  return sentence + ".";
}

The difference between this and the JavaScript equivalent is the type annotation of : string in the arguments to the function. This tells the TypeScript compiler that a string is expected. This has two effects.

One is that it knows that sentence is a string, and it will check whatever you do in the function is valid for a string. In this case we are appending another string to it, which is fine.

Secondly, it checks that any code calling this function is passing in a string and not something else. If this check fails it will fail to compile. With Javascript you wouldn’t know until you run the program that there is a problem.

Another interesting thing TypeScript will do with the code above is it will figure out that you will always return a string. It knows this because it knows that sentence is a string, and you are appending another string to that and returning it.

For any code that calls addPeriod and puts the result in a variable, TypeScript will know that variable is a string.

For example the following code will give an error when trying to compile:

let result = addPeriod("This is a sentence");
result = result * 3;

Here is what the error message looks like when viewed in the online TypeScript compiler:

TypeScript Code

The escape hatch

The nice thing about TypeScript is you can sometimes cheat and use it like good ol’ Javascript.

Javascript is excellent for dealing with responses from a server, which may have differently formatted data, missing data, etc. It is good because it treats the response like any other JavaScript object and you can call any properties you like without having to define them in advance. If you ask for something that is not defined, it’s value will be the undefined value that is well known in JavaScript.

For example if you get an object for the result of a soccer game you can call say .teamAgoals and .teamBgoals to see the score. If later they change the response to also include other information, such as the time of the game, you can easily change the program to process that.

In TypeScript you can declare a variable as having type any and this will allow you to treat it like classic Javascript, and have it be an unknown type that you can probe to see what properties it has. Here is an example:

let serverResponse : any = { color: 'red', numbers: [1,2,3] };

// This will say 'red':
console.log(serverResponse.color);
 
// This will say undefined:
console.log(serverResponse.shapes);

This example will compile, even though we are referring to shapes which the TypeScript compiler could otherwise tell is not part of the serverResponse object.

Summary

Here we have scratched the surface of TypeScript, a programming language that allows you to write JavaScript programs with additional safety checks so that you will make fewer mistakes in your JavaScript programs.

Check out this great book:

See Also