Understanding Types in TypeScript

I recently read this hilarious article about what it means to be a JavaScript developer in 2016.

The summary – working with JavaScript involves knowing a lot more than JavaScript. Remember the old days where we imported jQuery through a CDN and used that to manipulate a few things in the DOM or run some AJAX requests? That’s changed.

If you’re working with any modern JavaScript framework, you’ll most likely be writing code in ES6 or TypeScript or JSX, maybe using a templating engine like handlebars. You’ll also be using module loaders (like webpack) and automation tools (like grunt or gulp) along with package managers (mainly npm).

Now even though that may sound like a lot of work, it isn’t a bad thing! These tools and high-level frameworks can save you a lot of time and trouble.

Today we’ll talk about TypeScript and how you can work with it’s types.

Quick Overview of TypeScript

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
Any browser. Any host. Any OS.

Now that’s definitely a mouthful. Let’s give that some context.

JavaScript was never meant to be used as the core language for building large scale web applications. In the past, building large scale applications meant working with a language like Java or C# – .net (just to name a few). Such languages usually leaned towards “strongly typed” (in a strongly typed language, each type of data is predefined, that means that once x is a string, it’ll continue to be a string in it’s current scope).

With such languages, you save a lot of time by avoiding runtime errors. But that’s not the case with JavaScript. Slam your keyboard if this has ever happened to you.

or maybe this

JavaScript was used to add a few quirks to the HTML page. It was just something you needed to use when you needed additional client side functionality. Which is why it made sense for it to be a “loosely typed” language.

But times have changed now. Today, so many developers are using JavaScript full-stack for large applications. That means that there is some room for improvement.

That’s exactly what TypeScript aims to do. The benefits of a strongly typed language that compiles to JavaScript.

Try It!

If you want to try TypeScript right now, you can install the TypeScript Compiler (tsc) by following the quick start intructions.

Or you could just punch in the code samples without having to install any packages with the TypeScript playground.

What are Types

Types are what makes TypeScript great. They prevent many runtime errors and allow IDEs to do their magic and show you where the errors lie.

If you’ve worked with other object oriented programming languages (like Java), everything we talk about below will hit right home. If all you’ve ever known is JavaScript, read on!

Take a look at this code.

1

2

3

4

5

6

varx='something';

vary=3;

x=y;

console.log(x);

JavaScript is probably one of the very few places you can do something like this. If we were working with an object oriented language (the example below is in Java), this is what the same code would look like.

1

2

3

4

5

6

7

8

9

10

classMain{

publicstaticvoidmain(String[]args){

Stringx="something";

inty=3;

x=y;

System.out.print(x);

}

}

And this is what the console output would be.

You may say… “That just makes things harder.” And you’d be right.

For a simple program like this, it doesn’t matter, but when things more complex, you want compile errors to save you from annoying runtime errors. Let’s take a look at one more example.

We wouldn’t see a problem until our program reached this bit of the code during runtime…

1

2

3

4

5

varx=[1,2,3,"I don't belong here",4,5];

for(vari=0;i&lt;x.length;i++){

doSomethingImportantWithNumber(x[i]);

}

We’d know about the error as soon as the program would compile…

1

2

3

4

5

6

7

8

9

classMain{

publicstaticvoidmain(String[]args){

int[]x={1,2,3,"I don't belong here",4,5};

for(inti=0;i&lt;x.length;i++){

doSomethingImportantWithNumber(x[i]);

}

}

}

Imagine what will happen when this application gets larger!

Basic TypeScript Types

Even though we’ve been giving examples using Java, a more “strongly typed” language, that doesn’t mean TypeScript and Java are the same.

TypeScript is a superset of JavaScript. Which means that it allows us to work with the awesomeness of JavaScript. If you’re used to working with other object oriented languages, don’t be surprised if you see a data type that you’ve never seen before.

let

Before you can make sense of the examples below you need to know a bit more about the let keyword.

let‘s run through this really quickly!

Here’s how you declare a variable in JavaScript.

1

varx='hello';

Here’s the problem…

1

2

3

4

5

6

7

8

9

10

11

12

functionquirkyScope(){

varx='hello';

if(true){

varx='bye bye';

console.log(x);

}

console.log(x);

}

quirkyScope();

You’d expect the output of this to be…

1

2

bye bye

hello

But the real output is…

1

2

bye bye

bye bye

That’s because JavaScript has very different scoping rules compared to other languages.

let is a block scope variable declaration present in TypeScript (and ES6). When it compiles down to JavaScript, all it does is rename the variables to make it seem as though we are using block scope variables. Even if we aren’t.

Here’s a crash course in using let with the same example…

1

2

3

4

5

6

7

8

9

10

11

12

13

// We just change 'var' to 'let'

functionnormalScope(){

letx='hello';

if(true){

letx='bye bye';

console.log(x);

}

console.log(x);

}

normalScope();

That gives us the expected output…

1

2

bye bye

hello

It also rids us of the other scope-related quirks of JavaScript. You can read more about lethere, but for now, this is all you need to know.

1.Boolean

This doesn’t need any explaining, does it? True or false?

1

let isReady:boolean=false;

2. Number

All numbers in TypeScript are floating point values. This means that whole numbers, as well as decimals, come under the type number.

1

2

let wholeNumber:number=5;

let decimalNumber:number=5.25;

TypeScript also allows us to declare hexadecimal, binary and octal literals.

There are three things you need to notice in the templateString above –

It is surrounded with backticks (`).

We can use expressions within such a string using the ${expressionName} syntax.

We can use single or double quotations within these strings.

To use backticks in template strings, just escape them with \.

1

let iWantToUseBackticks:string=`Here'sabacktick-``

4. Array

There are two ways to type protect an array…

1

2

3

4

5

// First method

let numbersArray:number[]=[1,2,3,4,5];

// Second method

let anotherArray:Array=[6,7,8,9,10];

But what if you wanted more than one type in your array – maybe a string and a number?

5. Tuple

These data types allow you to define an array where we know the type of a fixed number of elements. Here’s an example…

1

2

3

4

5

6

7

8

9

10

11

let tupleArray:[string,number,number];

tupleArray=['add',2,3];// GOOD!

tupleArray=['multiply',3,4];// GOOD!

tupleArray=[2,3,'multiply'];// ERROR!

tupleArray=['add',2];// ERROR!

tupleArray=['add',2,3,4,'subtract',6]// GOOD?

Any additional elements in the array are OK, but they must be a union type – i.e. either of the types we have specified in the tuple.

In our example, our tuple has two types – string and number. So any of the elements after the first three must either be a stringor a number.

We’ll dive deeper into union types when we cover the advanced types in TypeScript in another article.

6. Enum

Enums are a friendly way of declaring numeric values. Have a look at this example where we only want to allow relatively sober people into our club to avoid any nonsense.

1

2

3

4

5

6

7

8

9

10

11

12

13

enumalcoholLevel{Tipsy,Drunk,Wasted,SuperWasted};

// Allow person if they aren't drunk

functionguardCheck(level:alcoholLevel){

if(level&gt;alcoholLevel.Drunk)console.log('no entry');

elseconsole.log('enjoy your night');

}

let stacyLevel:alcoholLevel=alcoholLevel.Drunk;

let andreaLevel:alcoholLevel=alcoholLevel.SuperWasted;

guardCheck(stacyLevel);

guardCheck(andreaLevel);

You can see how this is better than using constant or numbers directly. This makes the code much more readable and very safe.

By default, TypeScript assigns these enums values starting at 0. If for some reason we wanted to change that, we could…

1

2

3

4

5

// Starting at higher values

enumalcoholLevel{Tipsy=2,Drunk,Wasted,SuperWasted};// Tipsy = 2, Drunk = 3 and so on

// Different values

enumalcoholLevel{Tipsy=25,Drunk=50,Wasted=75,SuperWasted=100};

We can also get the name of the value in the enum from the numeric value.

1

2

3

4

let johnDrunkPercent:number=63.5;

let roundedUpPercent:number=75;

console.log(alcoholLevel[roundedUpPercent]);// output: Wasted

7. Any

There are cases where we may not know the types we are working with. That’s when we can use the any type.

1

2

3

4

let random:any="I can be of any type. TypeScript cannot stop me!";

// Useful in arrays where we can't predict all the values

let unpredictableArray:any=["What's the time?",4,false]

Any is even more flexible than a JavaScript Object. We can’t use any methods other than those that are a part of JavaScript’s Object interface (like toString) – even if we define them explicitly.

1

2

3

4

5

6

7

8

9

10

let anyObject:any=4;

anyObject.someMethod();// GOOD!

let objObject:Object={someMethod:()=&gt;{console.log('this is some method');}};

objObject.someMethod();// ERROR!

obObject.toString();// GOOD!

// Because toString is a part of JavaScript's object interface and all data types extend a JavaSCript object

// You can read about this method here - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString

8. Void

You use void when there is no type. This is useful when you are adding types to functions that don’t have a return value.

1

2

3

functionlogger(value:string):void{

console.log(value);

}

You could use void to declare variable types as well, but that isn’t very useful because only null and undefined can be of the type void.

Types on Functions

We didn’t add types to the return values in any of the functions above to keep the examples simple. Here’s how you would use functions with a return type.

1

2

3

4

functionsayHi():string{

return'hey';

// return 3 would give an ERROR!

}

9. Null and Undefined

null and undefined are subtypes of all other types. That means let value: number = null; is allowed. You could also use them on their own like let nullVal: undefined = undefined;. As you can see, that isn’t very useful.

So why have these types at all?

They become useful when you use the --strictNullChecks flag. That doesn’t allow type (other than void) to be null.

1

2

3

4

5

6

// With --strictNullChecks flag

let val:number=null;// ERROR!

let anotherVal:void=null;// OK!

let thirdVal:number|null|undefined=null;// OK!

The last example uses the union type. We’ll talk more about that in the advanced TypeScript types (in another post).

10. Never

never is used for values that never occur.

Wait… What?

Look at this function…

1

2

3

4

5

functionlogger(value:string):void{

console.log(value);

}

console.log(logger('Hey there'));

This is what the output looks like…

1

2

Hey there

undefined

Almost every JavaScript function returns a type – even console.log. When we don’t explicitly return a type in a JavaScript function, we implicitly return void.

Now take a look at something completely different…

1

2

3

4

5

6

7

functionstartLoop(){

while(true){

console.log('I will never end!');

}

}

console.log(startLoop());

Our final log statement would never be executed! So the startLoop function never returns a type. It just goes on and on!