💡 This page contain affiliate links. By making a purchase through them, we may earn a commission at no extra cost to you.
How to Get Started With TypeScript

How to Get Started With TypeScript

Making your JavaScript strongly typed
Ferenc AlmasiLast updated 2021 November 11 • Read time 13 min read
JavaScript is weakly typed by its nature. This means data types are always inferred from the variable declarations. However, TypeScript is here to help us.
  • twitter
  • facebook
TypeScript

JavaScript is weakly typed by its nature. This means that data types are always inferred from the variable declarations. This makes the language really flexible, and easy to learn for new programmers who don’t want to worry about different data types. But there’s a downside to this flexibility.

This can cause unexpected errors in your applications that you can easily catch with TypeScript even in your IDE before compile time. Imagine you are working on a game and you have some scores to be displayed. The data is stored in a database on the server, which you request. You get it back as a number and based on the values of other DOM elements, you want to increment this number. So you get the innerText from the DOM element and add it to your score:

Copied to clipboard! Playground
const score = 100;
const bonus = document.getElementById('bonus').innerText; // Returns "20" as a string

// Score will be "10020"
score += bonus;

This isn’t quite the behavior you were looking for, but no errors will be thrown as JavaScript silently converts the number into a string. This could have been easily avoided.

TypeScript — a superset of JavaScript — can turn your JavaScript files into strongly typed TypeScript files. And since any valid JavaScript file also works as a TypeScript file, the learning curve is really shallow. You can type your already existing JavaScript code, incrementally as you go, without having to worry about breaking anything.


Get Started With TypeScript

To get started with TypeScript, you can install it globally by running npm i -g typescript in your terminal. With that installed, you should be able to transpile TypeScript files, using the tsc command in your terminal. Create a new file called index.ts, and add the following line, then run tsc index.js in the directory where the file has been created:

Copied to clipboard!
const hello = '👋';
index.ts

You will see that TypeScript will generate an index.js file next to your TypeScript file with the following content:

Copied to clipboard!
var hello = '👋';
index.js

It changed the const keyword to var, so we can be sure that TypeScript is now set up and is working. This happens because it transpiles your code down to ES3 by default. Of course, this code has nothing to do with TypeScript so far, the const keyword is part of ES6. It just shows that TypeScript can also transpile your code down to a different version of JavaScript. You can target the version among other things with CLI flags. For example, to transpile to ES6, you would do:

Copied to clipboard!
tsc index.ts -t ES6
tsc index.ts -target ES6

Both of them will work, and you get back the same variable with the const keyword. Of course, it would be super tedious to always write out these flags into the terminal, so TypeScript provides a configuration file, where you can configure every aspect of the compiler.

Configuring TypeScript

For that, you’re going to need to have a tsconfig.json file at your project’s root. Here you can specify a compilerOptions node where you can set the target among many other things.

Copied to clipboard!
{
    "compilerOptions": {
        "target": "ESNext"
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
}
tsconfig.json

Here you also have the option to include or exclude certain files from transpiling with glob pattern support. For the full list of available compiler options, you can refer to the official docs.


Using TypeScript

Now that you have everything set up, let’s start using TypeScript. First, let’s see how you can define types for your variables. By default, variable types are inferred from the declarations. For example, if you have the following variable:

Copied to clipboard! Playground
const number = 5;
typeof number // returns "number"

const number = '5';
typeof number // returns "string"
number.js

It will be inferred as a number. JavaScript automatically assigns a type to it explicitly. If you change it to a string, then the type of the variable will be a string as well. In order to ensure we are dealing with a number, we need to assign a type using a double colon, followed by a type:

Copied to clipboard!
const number: number = 5;
number.ts

If you transpile your file into JavaScript, you will notice you won’t get any additional code in your output. You will only have var number = 5;. TypeScript checks the types at compile time. If you change the value of the variable to a string now, you will get an error, and you won’t even be able to transpile your ts file.

Trying to assign a string to a number type.

Apart from the number type, you have many other types you can use. Some of the more commonly used that you need to be aware of are:

Tuples

You know most of these already from JavaScript, but there are a couple of other types we need to talk about. For example, a tuple is a fixed size array where you know the type of each element:

Copied to clipboard!
const tuple: [string, number] = ['0', 1];
tuple.ts

This way you won’t be able to add or remove elements from the array. Also if you are referencing one of the elements, you get the proper object methods. For example on tuple[0] you will get an error if you try to use toFixed as it is not a string, but a number. And when you are dealing with regular arrays, you have two ways to type them:

Copied to clipboard!
// Use the type of the element followed by []
const emojis: string[] = ['1️⃣', '2️⃣', '3️⃣'];

// Use the generic array type: Array<type>
const emojis: Array<string> = ['1️⃣', '2️⃣', '3️⃣'];
arrays.ts

Enums

Enums are another helpful addition to JavaScript. With enums, you can create a set of values with more meaningful names. Take the following as an example:

Copied to clipboard!
enum Direction {
    Up,
    Left,
    Down,
    Right
}
enum.ts

Here, each value is a numeric value, starting from 0. This means that Right will be equal to 3. If you transpile this down and look into the JavaScript file, you will see the following generated:

Copied to clipboard! Playground
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Left"] = 1] = "Left";
    Direction[Direction["Down"] = 2] = "Down";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
enum.js

You can access the values just like you would for any other object in JavaScript.

Copied to clipboard!
Direction.Up
Direction.Down
... and so on ...

You can also change the starting value by assigning a number to it, or if you prefer, you can assign a value for each of them to further improve readability.

Copied to clipboard!
// Starting from 1 instead of 0
enum Direction {
    Up = 1,
    Left,
    Down,
    Right
}

// Asigning a value to each of them for better clarity
enum Direction {
    Up = 1,
    Left = 2,
    Down = 3,
    Right = 4
}

// You can also use strings as values
enum Direction {
    Up = 'UP',
    Left = 'LEFT',
    Down = 'DOWN',
    Right = 'RIGHT'
}
enum.ts

Any

One of the best features of TypeScript is the any type. The any type means that your type can be anything. You either don’t know it upfront or you don’t care. You would use this type when you are dealing with 3rd party code, or you are using an API, where you don’t know the return type. It’s also comes really handy when you want to migrate a big project from JavaScript to TypeScript. You can opt-out of type checking certain variables if you need to deliver code changes in a strict time schedule.

Copied to clipboard!
let variable: any = "I'm a string";

variable = 5      // Maybe I'm a number
variable = false; // Or am I a boolean?
any.tsUsing any, you won't get any compile errors
Using any, you won't get any compile errors

Custom Types

Apart from the built-in types of TypeScript, you can also define your own types. So far, we’ve seen how to make a variable typed. But what if a variable can take up multiple types? For this, you can use a pipe (|):

Copied to clipboard!
const easing: 'LINEAR' | 'EASE-IN' | number = 'EASE-OUT';

The above example would throw an error as the type can be either a string (one of LINEAR or EASE-IN) or a number. To outsource this type and reuse it elsewhere, you can use the type keyword to define it as a custom type:

Copied to clipboard!
type Easing = 'LINEAR' | 'EASE-IN' | 'EASE-OUT' | number;

const easing: Easing = 'EASE-OUT';
easing.ts

Now this won’t throw any errors as we can also use EASE-OUT. This especially comes useful when you are dealing with more complex objects:

Copied to clipboard!
type HealthBar = {
    max: number,
    segment: number
}

type Enemy = {
    health: HealthBar,
    name: string,
    damage: number,
    alive: boolean
}

const enemy: Enemy = { ... }
types.ts

Note that you can also nest different types into each other. And if you incorrectly define your variable, you also get helpful error messages on what went wrong, and how you should fix it.

Type error in typescript
TypeScript warning, I’m missing the segment node from health

But what if you want to omit some of the properties for other types of enemies? Luckily, you don’t need to create entirely new types, instead you can use optional properties with a question mark before the double colon:

Copied to clipboard!
type Enemy = {
    health: HealthBar,
    name: string,
    damage: number,
    alive: boolean,
    boss?: Boss
}
types.ts

Here the boss property is optional, meaning even if you don’t pass it to your objects, TypeScript will still compile.

Functions

Functions are a fundamental building block in JavaScript. We’ve talked about a couple of basic types so far from the list above, but we haven’t touched the void type yet. When you are dealing with functions, you will most probably see void a lot. The type void infers that a function has no return value. Take the following as an example:

Copied to clipboard!
const addClickEventListener = (e: Event): void => { ... }

Here you are specifying the type of the return value of the function, after the parentheses. Since there is no return value for an event listener, the void type is used. Just like we did previously with variables, you can also type the parameters.

Copied to clipboard!
const add = (a: number, b: number): number => a + b;

Here the function returns a number, and both of the parameters are also typed as numbers.

Interfaces

Another common data type you’re going to find in TypeScript is interfaces. Very similar to the type alias, but unlike them, you can’t use interfaces for primitives, only for objects.

Copied to clipboard!
// ✅ this works
type Easing = 'LINEAR' | 'EASE-IN' | 'EASE-OUT' | number;

// ❌ this doesn't
interface Easing = 'LINEAR' | 'EASE-IN' | 'EASE-OUT' | number;

// ✅ this is valid
interface Easing {
    type: 'LINEAR' | 'EASE-IN' | 'EASE-OUT' | number;
}
objects-vs-primitives.ts

Also, interfaces are always extensible, and multiple interfaces with the same name will be merged together. This is not something you can do with type aliases.

Copied to clipboard!
interface Easing {
    type: 'LINEAR' | 'EASE-IN' | 'EASE-OUT' | number;
}

interface Easing {
    timing: number
}
merging.ts

These two interfaces will be merged together into one that has both a type and a timing property. You cannot do this with types.

Multiple type aliases with the same name.
You will get an error if you try to redefine a type alias with the same name.

What you can do instead, is use an ampersand to extend a base type in the following way:

Copied to clipboard!
type Easing = {
    type: 'LINEAR' | 'EASE-IN' | 'EASE-OUT' | number;
}

type EasingWithTiming = Easing & {
    timing: number
}

// The same can be done with interfaces using the `extends` keyword
interface EasingWithTiming extends Easing {
    timing: number
}
extending.ts

If you go ahead and transpile your file now to JavaScript, you will notice that nothing is generated for interfaces or type aliases, the compiler only uses this to check validity at compile-time, so it won’t affect your performance and bundle size in any way.

Classes

Classes are another extension in TypeScript. ES6 also introduced the class keyword, but for example, it lacks modifiers such as the public, private or protected keywords.

Copied to clipboard!
class Widget {
    private isCollapsed: boolean;
    
    constructor(collapsedByDefault: boolean) {
        this.isCollapsed = collapsedByDefault;
    }

    getState(): boolean {
        return this.isCollapsed;
    }
}
class.ts

You may notice we have only defined a private modifier, but nothing for the constructor and the getState method. In TypeScript, each member is public by default, so you can omit them. Again, if we transpile this code, we can see what TypeScript generates for us. In this case, all the way down to ES3.

Copied to clipboard! Playground
var Widget = /** @class */ (function () {
    function Widget(collapsedByDefault) {
        this.isCollapsed = collapsedByDefault;
    }
    Widget.prototype.getState = function () {
        return this.isCollapsed;
    };
    return Widget;
}());
generatedClass.js

Generics

Lastly, I wanted to talk a bit about generics. When building applications, you often have to think about how to create reusable components, that also have a well-defined API. These components should work on a variety of types, instead of only handling just a single one. That makes them so flexible. You might be thinking the simplest way to create a generic is by using the any type.

Copied to clipboard!
const fn = (param: any): any => param;

And while that is true, this function will accept any type as an argument and can return anything, in the meantime, you lose information about what type is passed and what type is returned. You can know that a number is passed to the function, but you can’t be sure that a number is returned as well. It can be anything. Instead, what you can do is use a type variable to identify what type is being passed to the function, and what type is returned:

Copied to clipboard!
function fn<T>(param: T): T {
    return param;
};
generic.ts

Here, T identifies the type. You can then call this function in two ways with a specific type:

Copied to clipboard!
fn<number>(10); // Passing the type argument
fn(10);         // let TypeScript automatically set the type of `T` based on the argument
The latter option is more commonly used
Looking to improve your skills? Master TypeScript from start to finish.
Master TypeScriptinfo Remove ads

Conclusion

And now you know everything you need to know to get started with TypeScript. If you find yourself stuck while using it, I highly recommend checking out their official documentation, it should have all the necessary information you need.

Have you worked with TypeScript before? Let us know your thoughts about it in the comments down below! Thank you for reading through, happy coding!

  • twitter
  • facebook
TypeScript
Did you find this page helpful?
📚 More Webtips
Frontend Course Dashboard
Master the Art of Frontend
  • check Access 100+ interactive lessons
  • check Unlimited access to hundreds of tutorials
  • check Prepare for technical interviews
Become a Pro

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.