TypeScript Tutorial: What is TypeScript [Complete Overview]

By Sruthy

By Sruthy

Sruthy, with her 10+ years of experience, is a dynamic professional who seamlessly blends her creative soul with technical prowess. With a Technical Degree in Graphics Design and Communications and a Bachelor’s Degree in Electronics and Communication, she brings a unique combination of artistic flair…

Learn about our editorial policies.
Updated March 9, 2024

This TypeScript tutorial presents a complete overview including what is TypeScript, environmental setup, generics, benefits, etc. Also, explore the key difference between TypeScript and JavaScript:

This tutorial will give you a complete overview of TypeScript in simple terms that is easy to understand.

All the knowledge you have around JavaScript like arrays, objects, functions, and even ES 2015 syntax like destruction, arrow functions, and classes still apply to the world of TypeScript as well.

Here we will guide you to understand the type system, TypeScript environmental setup, compiler, and other necessary things to learn.

Also, compare the popularity and understand the differences between TypeScript and JavaScript.

Complete TypeScript Tutorial

List of Tutorials in this TypeScript Series:

Tutorial #1: TypeScript Tutorial: What is TypeScript – Complete Overview [This Tutorial]
Tutorial #2: TypeScript Map Type – Tutorial With Examples
Tutorial #3: How to Use Create React App With TypeScript
Tutorial #4: TypeScript Interface Tutorial With Examples
Tutorial #5: TypeScript Enum Tutorial – How to Use Enum in TypeScript
Tutorial #6: 42 Most Common TypeScript Interview Questions And Answers
Tutorial #7: TypeScript Vs JavaScript – What Are The Key Differences


TypeScript Tutorial

The only thing that we’re doing with TypeScript is adding a little additional syntax to our code to handle, something called the type system.

type system

The goal of the type system is to help us catch errors during development.

ts type system

To do this error checking, the TypeScript compiler is going to use type annotations to analyze our codebase.

We can think of TypeScript as just being plain JavaScript, but with these type annotation things added in, then once we want to run our code, we’re going to feed that code into the TypeScript compiler.

Javascript plain code

Environment Setup

It’s now time to do a bit of environment setup. Once we set up our local machine to work with TypeScript. The first thing is to install the TypeScript compiler. To install it, we’re going to use npm to install TypeScript as a global module.

Proceed and open up the terminal. Run npm install -g and get TypeScript and ts-node as shown in the code below:

npm install -g typescript ts-node

And then run the compiler by running the code snippet below:

tsc –help

We installed a package called TypeScript. It gives us access to a command called tsc, which is short for, as you might guess, the TypeScript compiler. This is what we’re going to use to compile our TypeScript code into plain JavaScript.

When you run this command, see a bunch of Help messages appear on the screen as shown in the image below:

bunch of Help messages

If you see any error, that means that your npm installation probably has a minor issue and you’ll need to do a bit of troubleshooting. That’s it for the actual TypeScript setup.

TypeScript Compiler

The first thing we’ll do is create an index.ts file. TypeScript code on its own can’t run anywhere. It won’t work in the browser or in NodeJS or anything like that.

What we do is use the TypeScript compiler to convert that TypeScript code to vanilla JavaScript. Let’s start by writing some plain JavaScript in our TypeScript file and then compiling it.

We’ll just type:

console log Hello world

Then we’ll go down to the command line and run

tsc index.ts

You’ll notice that it creates an index.js file that is our actual JavaScript code we can run in the browser or node.

And because we just wrote plain JavaScript, the code is identical to what’s in the index TS file as shown below:

index TS file

By default, TypeScript will compile to ES 3, which doesn’t have support for Async await. Let’s see what happens when we write an Async function in our tsc file and then compile it.

sync function hello() {
  return 'world'
}

You’ll notice as shown on the image below that our code gets trans piled to this crazy-looking JavaScript just so we can use Async await in our main TypeScript code.

index

The compiler is actually very sophisticated and there is a tonne of different options that you can pass to it to customize its behavior.

Autocomplete and IntelliSense

You could pass these options in from the command line, but the standard way to do it is to create a tsconfig.json, which will automatically get picked up when you run tsc as shown below:

create a tsconfig.json

The tsconfig can seem pretty overwhelming at first, but there are usually only a few options that you have to think about. Mostly, the first one is the target, and this is the flavor of JavaScript that your code will be compiled to.

If we set our target to ES next and then run tsc, you’ll see that it compiles our code with Async Await natively because it’s targeting the latest version of JavaScript, which supports that syntax.

Another option that we’ll want to set right away is Watch True, which will just re-compile our code every time we save the file that’ll just save us from rerunning the tsc command after every change.

{
"compilerOptions": {
     "target": "esnext",
     "watch": true
   }
}

The next option we’ll look at is Lib, which allows us to automatically include typing for certain environments such as the DOM or ES 2017.

If you’re building a web application, you’d want to include the DOM library, which allows TypeScript to compile your code with all the native Dom classes without any compilation errors.

{
"compilerOptions": {
   "target": "es3",
   "watch": true,
   "lib": ["dom", "es2017"]
  }
}

For example, if we go back to our code, we can use the URL class, which is part of the Dom, and we’ll get Autocomplete and IntelliSense on this class as shown in the image below.

This is where the incredible tooling of TypeScript comes in.

Autocomplete and IntelliSense

If we hover over the class, we have integrated documentation and an error message telling us exactly why this code won’t run.

If we want a really explicit view of the interface, we can right-click and go to the typing.

We’ll now have a view of every property and method that exists in this class, but usually, that is not necessary because all this stuff will just auto-complete for you as you start typing.

Third-Party Libraries

The next thing we look at is the use of third-party libraries. Let’s go ahead and install lodash with npm, and you’ll see that it creates a Node Modules folder with the source code for lodash.

npm i lodash

A lot of mainstream libraries like Firebase, for example, ship with type declarations automatically, but lodash is not one of them.

If we go into our index TS and import lodash, we’ll get a warning from TypeScript saying that there are no declarations found, which means we will not get any autocomplete or IntelliSense in the IDE.

The good news is there’s a giant mono repo out there with community-maintained types. If we install the types in our development environment, we’ll have Autocomplete and IntelliSense for every lounge.

npm i -D @types/lodash

Now that we know how the TypeScript compiler works, let’s go ahead and write some code that uses type annotations. There are two ways you can strong type your code implicitly or explicitly.

Implicitly and Explicitly Typing

Let’s say we have a variable that should be a number. If we assign a value to this variable when it’s declared, its type will automatically be inferred. As you can see below, it’s a primitive number type.

let lucky = 23;

number

Then if we go down and try to assign a string value to this variable, it’s going to give us an error because a string is not assignable to a number.

let lucky: number
lucky = '23';

assign a string value

If this code were vanilla JavaScript, we wouldn’t catch this bug until we actually run our code somewhere.

But with TypeScript, we know about it right away. Unlike languages like C Sharp or Java, we can actually opt-out of the type system by annotating our variable with any.

let lucky: any = 23;
lucky = '23';

This just means that we can assign this variable any value, and the compiler won’t type cheque it. Ideally, you want to avoid doing things like this when possible, but it does give TypeScript a tonne of flexibility.

In the last example, we gave our variable an implicit number type.

But what if we don’t have a value to assign to it upfront? If we don’t add any type annotations to it, it’s going to be inferred as in any type, we can assign both a string and a number to it.

If we want to annotate it with a type, we can just do a colon followed by a number, which is one of the built-in primitive types in JavaScript. When we do that, we get an error under the string value because we can’t assign it as that type.

let lucky: number;
lucky = '23';
lucky = 23

explicit

One tip that we’ll give you here is that if you have an implicit type, don’t bother explicitly strong typing it.

For example, here we’re assigning a value that is a number. Adding the number annotation is really just redundant. We’ve looked at some of the built-in types in JavaScript, but you can also create your own types from scratch.

First, you’ll give the type of name, which is typically in Pascal’s case. And for right now, we’ll just go ahead and arbitrarily assign our style type to a string.

type Style = 'string';

let font: Style;

Then we can declare a variable that is annotated with this style type, and then we’ll get feedback for this custom type instead of just a regular string. Right now, this is super redundant, but let’s say our style type can only be bold or italic.

We can create a Union type by separating them with a pipe, and now we can only assign this variable to these two specific values, and we’re not limited to just strings.

We could even extend this custom type with a number, so that is pretty cool, but more often than not, you’ll be strong typing objects that have multiple properties with multiple different types.

type Style = 'bold' | 'italic' | 23;

let font: Style;

font = 'something'

Let’s imagine we have two objects, and we want to enforce that this object shape has a first and last name with String types.

Composing objects or class instances that don’t have the correct shape is an easy way to create bugs in your program, but with TypeScript, we can enforce the shape of an object with an interface.

If we know that the shape of the person object will always be the same, then we can define an interface that defines the types of each property.

We can use this interface to strong type these objects directly, or we could use it as the return value from a function or as an argument, or anywhere else in our code.

Sometimes an interface like this can be a little too restrictive, but you can actually maintain the required properties and then add any additional properties to be added by creating a key with a type of string with a value type of any.

Now a first and last name will be required, but you can also add any additional property that you want to this object.

interface Person {
first: string;
last: string;
[key: string]: any
}

const person: Person = {
first: 'Jeff',
last: 'Delaney'
}

const person2: Person = {
first: 'Usain',
last: 'Bolt',
fast: true
}

Functions

Now let’s go ahead and switch gears to functions. Strong typing of a function can be a little more complex because you have types for the arguments and also the return value.

Below, we just have a plain JavaScript function without any types that raise X to the power of y:

function pow(x , y) {

return Math.pow(x, y);

}

Currently, we could add string values as the arguments, and we wouldn’t get any error from the compiler.

function pow(x , y) {

return Math.pow(x, y);

}

pow('23', 'foo')

But obviously, this function is going to fail if we try to pass it to any non-number value.

You can annotate arguments the same way we do with variables by just adding a colon and then the type after it, and that will ensure that only numbers can be passed to this function.

function pow(x: number, y: number) {

return Math.pow(x, y);

}

pow(5, 10)

The function implicitly has a number return value because we’re using the native math JavaScript library, but we can annotate a specific return value type after the parentheses and before the brackets.

function pow(x: number, y: number): string {

return Math.pow(x, y);

}

pow(5, 10)

If we set that type to a string, you’ll see it’s underlined in red because it’s returning a number. To implement this function correctly, we can call toString, which will then clear out that underlined error.

function pow(x: number, y: number): string {

return Math.pow(x, y).toString();

}

pow(5, 10)

In many cases, you might have functions that don’t return a value or create some kind of side effect. In that case, you can type your function return value to void.

function pow(x: number, y: number): void {

Math.pow(x, y).toString();

}

pow(5, 10)

You’ll commonly see the void type of functions like event listeners or side effects that just don’t return a value. The next thing we’ll look at is how to strong type an array.

Arrays

We’ll start by creating an empty array and then pushing a few different values to it.

const arr = []

arr.push(1)
arr.push('23')
arr.push(false)

With different types, we can force this array to only have number types by doing a number type followed by brackets signifying that it’s an array.

const arr: number[] = []

arr.push(1)
arr.push('23')
arr.push(false)

Now you can see we get an error every time we try to push a value that is not a number. This is especially useful when you’re working with an array of objects and you want to get some IntelliSense as you’re iterating over those objects.

For example, if we retrieved an array of people from our database, we could use a person interface to know the exact shape of those objects as they’re retrieved.

Tuples

TypeScript also opens the door to a new data structure called a tuple. These are found in other programming languages like Python.

Basically, they’re just a fixed-length array where each item in that array has its own type, so we can name us type my list, and then we’ll give each of its values a different type.

type MyList = [number, string, boolean]

const arr: MyList = []

arr.push(1)
arr.push('23')
arr.push(false)

Currently, we get an error because we’re initializing this array as an empty array, but the compiler is expecting all these values to be defined upfront, one thing you can do is make these values optional by putting a question mark after the type.

type MyList = [number?, string?, boolean?]

const arr: MyList = []

arr.push(1)
arr.push('23')
arr.push(false)

And you can also use this question Mark syntax in other places in TypeScript, for example, to make function arguments optional.

TypeScript Generics

The last thing we want to show you is TypeScript generics. You may run into situations where you want to use a type internally inside of a class or function.

A good example is an RxJS observable, which itself is just a class that has an internal value that you can observe.

class Observable<T> {
     constructor(public value) {}
}

The capital T in this code represents a variable type that we can pass into the strong type of these observables’ internal value.

class Observable<T> {
constructor(public value: T) {}
}

This allows us to specify the internal type at some later point in our code. For example, we might have an observable of a number, or maybe we have an observable of our person interface, and we could also do this implicitly.

If we create a new observable of a number, it’s going to implicitly have that internal number type. So more often than not, you’ll be using generics rather than creating them, but it’s definitely an important thing to know.

//Generics

class Observable&lt;T&gt; {
constructor(public value: T) {}
}

let x: Observable&lt;number&gt;;

let y: Observable&lt;'Person'&gt;;

let z = new Observable(23)

Hopefully, this tutorial gave you an idea of why TypeScript is so powerful.

Benefits of TypeScript

The biggest benefit is actually just tooling what you get in your IDE like Visual Studio Code. When you use type annotations or work with libraries that are strongly Typed, your code will be automatically documented in the IDE.

You rarely have to refer to online documentation for the libraries that you use.

In addition, the compiler can catch bugs in advance, which is a far more efficient way to refactor code.

Would you rather have silly errors during development or insanity-inducing errors in production? Another cool benefit of TypeScript is that there’s virtually no learning curve.

If you know JavaScript, that is because it’s a superset of JavaScript, so any valid JS code is also valid in TypeScript so you can learn it incrementally as you go.

Last, it also allows us to write our code with future JavaScript features without having to worry about whether or not this code will be supported in our environment because we can transpire it to multiple JavaScript flavors.

Differences: TypeScript And JavaScript

JavaScript and TypeScript are intended for building interactive web pages. This is one of the fundamental similarities between the two languages.

The table below highlights a few differences:

TypeScriptJavaScript
When writing TypeScript, we are just adding in little extra pieces of syntax meant to help the TypeScript compiler understand what we're trying to do with our code base, so it can help us catch errors.JavaScript interpreter interprets our code line after line while simultaneously executing them
When we write TypeScript, we're still writing JavaScript, we're just adding in these extra little type annotation things.JavaScript is JavaScript
TypeScript has no effect on how our code actually gets executed. In other words, there is no performance optimization or anything like that. JavaScript has effect on how our code actually gets executed
The best way to think of TypeScript is - It is a helper to help us catch errors.It’s executed and output generated
It doesn't actually have any impact on the final code that we output or the actual code that we run inside the browser. It's just there during development. It actually has an impact on the final code that we output or the actual code that we run inside the browser.

Typescript: Pros And Cons

ProsCons
Errors are highlighted during compilationHeavier than TypeScript (Long compilation time)
Integration is supportedUsed for ONLY large complicated applications
Popularity is growing very fastExtra Build-up procedure
Most suited for large complicated applicationsOnly checks for type errors - cannot run on its own, JavaScript is the one that gets executed
Uses JavaScript latest features

The popularity of TypeScript and JavaScript

Popularity of TypeScript and JavaScript

For nine years in a row, it has maintained JavaScript as the most used programming language worldwide, with 64.96%. TypeScript is in ninth place with 30.19%.

Conclusion

The way we write TypeScript is the same way we write JavaScript except that we add “extra documentation”.

What we do is use the TypeScript compiler to convert that TypeScript code to vanilla JavaScript. The manner in which code is executed by Node or browser is not in any way affected by Typescript.

A good example of what Typescript is would be that buddy of yours sitting beside you while getting your hands dirty coding. To learn more visit here.

NEXT Tutorial

Was this helpful?

Thanks for your feedback!

Leave a Comment