Lesson Weekend

Now that we know a little bit about TypeScript, its origins, and the benefits it’ll offer us while developing our Angular applications, let’s start coding.

In this lesson we’ll create our very first TypeScript application together. We’ll practice using TypeScript independent of the Angular framework in order to both familiarize ourselves with its features and syntax, and to clearly differentiate between what new concepts are TypeScript-specific, and which belong to Angular.

Writing a TypeScript Application

We’ll begin by creating a project directory named my-first-typescript. Within this directory we’ll include:

  • An app folder. This is standard naming convention for source code directories in many frameworks. The majority of our code will reside here.

  • Within app create a file named hello.ts. As you may have guessed, the .ts extension stands for “TypeScript”.

We’ll include two brief lines of code in hello.ts:

my-first-typescript/app/hello.ts
var greeting: string = "Hi TypeScript!";
console.log(greeting);

The first line var greeting: string = "Hi TypeScript!"; defines a String named greeting with the contents ”Hi TypeScript!”. But there are a few things that differ from vanilla JavaScript here.

Instead of saying var greeting = ... like we would in JavaScript, we include the data type string. This designates that greeting can only contain strings.

Specifying a variable’s data type like this is known as optional typing. In TypeScript we have the option to define a variable’s data type!

Compiling TypeScript

As we discussed, TypeScript is compiled. Our browser won’t inherently understand our .ts files. We have to compile them into something it can understand first. Let’s do this now.

We can compile our TypeScript file by running the following command in the top level of the project directory:

$ tsc app/hello.ts

tsc stands for TypeScript compiler. We use the $ tsc command to invoke the compiler, and provide the location of the .ts file we’d like it to compile.

After running this command we’ll notice another file (hello.js) has appeared in the directory. We can open it, and view its contents:

This is the compiled version of the TypeScript we just wrote in hello.ts. The compiler compiled it into vanilla JavaScript, and placed it in a corresponding hello.js file.

We won’t ever need to read or edit this file directly. Only the browser will. Instead, when we need to make changes we’ll simply update our .ts file, and re-compile to create a new .js file.

Running Compiled TypeScript in the Browser

Now, let's run the compiled file in the browser. We'll make an index.html file in the top level of our project directory, and load hello.js file into it with a script tag:

my-first-typescript/index.html
<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="app/hello.js"></script>
    <title>Hello TypeScript!</title>
  </head>
  <body>
  </body>
</html>

Let's launch the index.html file in the browser and open the console to confirm that it works. If we open the console, we should see our greeting has been logged successfully!

typescript-log-message

We just wrote and compiled our first TypeScript application!

Optional Typing

A moment ago we learned the : string in the line var greeting: string = … was TypeScript’s optional typing feature. Let’s explore optional types further before we move on.

Note: Again, if you’ve taken PHP, C# or Java you’re likely familiar with this concept. Take this time to review and solidify TypeScript-specific syntax, and use your experience to assist classmates who haven’t yet coded in a typed language!

The primary benefit of optional typing is that it can prevent errors before they ever occur! How does this work? Let’s see for ourselves.

Potential Issues with Loosely-Typed Languages

Create an additional file in the app folder named vanilla-js-addition.js. Place the following code inside:

my-first-typescript/app/vanilla-js-addition.js
var firstNumber = prompt('Please enter a number.');
var secondNumber = prompt('Please enter another number.');
var sum = firstNumber + secondNumber;
alert("The sum of your two numbers is: " + sum);

As you can see, this is plain old JavaScript, not TypeScript. “Plain” JavaScript like this is often referred to as vanilla JavaScript.

The code above should accept two numbers from the user, add them together, and return the result in an alert box.

However, it contains a bug. Let’s see how this bug manifests in regular JavaScript. We'll then translate the same buggy code into TypeScript to see what happens differently.

Let’s link our buggy .js file in index.html:

my-first-typescript/index.html
<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="app/vanilla-js-addition.js"></script>
    <title>Hello TypeScript!</title>
  </head>
  <body>
  </body>
</html>

Next, we’ll launch our index.html file in the browser and enter the number 1 for the first prompt:

buggy-js-first-prompt

And 1 for the second prompt:

buggy-js-second-prompt

But wait, look what our program has returned! 1 + 1 is not 11!

buggy-js-result

Errors like these are symptoms of JavaScript being a loosely typed language. A language is loosely typed if it doesn't require variables to define their data type (ie: String, Integer, etc.) upon creation.

JavaScript doesn't actually care if firstNumber actually contains a number. It could contain a decimal, an integer, or even a string as far as it’s is concerned.

This can lead to problems like the one depicted above. Because we didn’t parseInt(), the firstNumber and secondNumber variables contained strings instead of numbers. When the line var sum = number + otherNumber; runs, it concatenates two strings instead of adding two numbers. You may remember this error from Intro to Programming.

TypeScript allows us to strictly define what data types our variables can contain. Doing so is technically optional. However, strict typing easily prevents errors like the one above. You are expected to declare data types for variables as we use TypeScript for the next two weeks.

Error-Catching Benefits of Compiled TypeScript

Let’s try recreating the same error in TypeScript. Create a typescript-addition.ts file in the app directory and include the following code:

my-first-typescript/app/typescript-addition.ts
var firstNumber: number = prompt('Please enter a number.');
var secondNumber: number = prompt('Enter another number.');
var sum: number = firstNumber + secondNumber;
alert("The sum of your two numbers is: " + sum);

As you can see, this code is almost identical to the vanilla JavaScript version. The difference is that firstNumber and secondNumber are declared as number data types.

Let’s link the .js file this .ts file will compile to in index.html:

my-first-typescript/index.html
<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="app/typescript-addition.js"></script>
    <title>Hello TypeScript!</title>
  </head>
  <body>
  </body>
</html>

Then we'll compile our new file:

$ tsc app/typescript-addition.ts

Before we can even attempt to run it in the browser, TypeScript gives us an error!

app/typescript-addition.ts(1,5): error TS2322: Type 'string' is not assignable to type 'number'.
app/typescript-addition.ts(2,5): error TS2322: Type 'string' is not assignable to type 'number'.

You’ll notice these errors include the location of the problem (the app/typescript-addition.ts file), and the specific line numbers in addition to the description ”Type 'string' is not assignable to type 'number'.”

Because we stated firstNumber and secondNumber must be numbers, the compiler identified an error: because we were using prompt(), the variables were strings instead of numbers. The compiler caught this bug and alerted us before we could run our code.

Optional Typing in Variables

Let’s fix our bug to get beyond this compiler error:

my-first-typescript/app/typescript-addition.ts
var firstNumber: number = parseInt(prompt('Please enter a number.'));
var secondNumber: number = parseInt(prompt('Enter another number.'));
var sum: number = firstNumber + secondNumber;
alert("The sum of your two numbers is: " + sum);

If we compile again, we get no errors. (Generally, if the terminal offers no messages after compiling, it means the command ran successfully.)

Note: Your text editor may display an error saying you require a tsconfig.json file. This is just a warning and will not prevent you from working in TypeScript. _tsconfig.json files are used to configure the compiler. We'll add these files when we work in Angular so you can ignore this message for now._

If we run our updated code in the browser and enter 1 in each prompt, we should now receive the correct result:

correct-addition-typescript

Optional Typing in Functions

We can also use optional typing when we're declaring a function. Let's refactor our code to use a method:

my-first-typescript/app/typescript-addition.ts
var findSum = function(firstValue: number, secondValue: number) {
  var sum: number = firstValue + secondValue;
  alert("The sum of your two numbers is: " + sum);
}

var firstNumber = prompt('Please enter a number.');
var secondNumber = prompt('Enter another number.');
findSum(firstNumber, secondNumber);

As you can see, we’re able to attach data types to each of the two parameters in our new findSum() method.

We can compile our updated code:

$ tsc app/typescript-addition.ts

And TypeScript will successfully warn us of our error:

app/typescript-addition.ts(2,7): error TS2322: Type 'string' is not assignable to type 'number'.
app/typescript-addition.ts(8,9): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

Again, TypeScript caught that we were attempting to assign a string value to something we declared could only hold numbers. We'll fix this error using parseInt():

my-first-typescript/app/typescript-addition.ts
var findSum = function(firstValue: number, secondValue: number) {
  var sum: number = firstNumber + secondNumber;
  alert("The sum of your two numbers is: " + sum);
}

var firstNumber = parseInt(prompt('please enter a number.'));
var secondNumber = parseInt(prompt('enter another number.'));
findSum(firstNumber, secondNumber);

In addition to ensuring we’re providing the correct type of data, TypeScript can also ensure we’re providing the right number of arguments.

Let’s pretend we erroneously called findSum() with only one number:

my-first-typescript/app/typescript-addition.ts
...

var firstNumber = parseInt(prompt('please enter a number.'));
var secondNumber = parseInt(prompt('enter another number.'));
findSum(firstNumber);

When we attempt to compile, it will warn us:

app/sum.ts(8,1): error TS2346: Supplied parameters do not match any signature of call target.

If you haven’t already encountered the term, a method signature is the combination of the method’s name and parameter list. This error message states the supplied parameters do not match the signature. Essentially, the arguments provided do not match the parameters the method takes!

This helps prevent runtime errors in our code. After all, we can't run a method without the correct number of required arguments.

Let's see what happens if we make this same error in vanilla JavaScript. We'll update our vanilla-js-addition.js file to contain the following:

my-first-typescript/app/vanilla-js-addition.js
var findSum = function(first, second) {
  var sum = first + second;
  alert("The sum of your two numbers is: " + sum);
}

var number = parseInt(prompt('please enter a number.'));
var otherNumber = parseInt(prompt('enter another number.'));
findSum(number);

And link it in index.html:

my-first-typescript/index.html
<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="app/vanilla-js-addition.js"></script>
    <title>Hello TypeScript!</title>
  </head>
  <body>
  </body>
</html>

If we reload index.html, we see that our file runs in the browser. But if we enter two values in the two prompts, we receive NaN because we attempted to add a number to an undefined variable.

The bugs we’ve experimented with in this lesson are rather small, but imagine if this was a function in a huge project. Perhaps this function was called in multiple functions and even in many different files. This error would be a lot harder to catch without the compiler’s warning.

Terminology


  • Optional typing: A feature of TypeScript that allows the developer to declare the data type of a variable.

  • Loosely typed: A description for a language that does not require the developer to define the data type of a variable (e.g. JavaScript).

  • Type annotations: Definitions for variables that initialize them with a type instead of allowing a type to be inferred.

Overview


  • TypeScript allows us to define what data type a variable or argument should be. This allows us to prevent errors before they occur, because the TypeScript compiler will let us know if our code is attempting to use an incorrect data type somewhere.

Example


Example of type annotations:

var findSum = function(first: number, second: number) {
  var sum = first + second;
  alert("The sum of your two numbers is: " + sum);
}

The first and second parameters have type annotations of number which enforces the rule that only numbers will be accepted in the function.