TypeScript For Beginners

A Practical Way To Learn TypeScript šŸ•šŸ•šŸ•šŸ•

Ā·

13 min read

TypeScript For Beginners

TypeScript is becoming more popular than ever. As a beginner, it was not love at first sight for me and TypeScript, but weā€™ve got to know each other. Currently, I donā€™t start a project without using TypeScript!

In this post, I want to dive into the basics of TypeScript. Weā€™re going to learn some theory, but since I believe in learning by building something, we are gonna be practical.

divider-byrayray.png

Table Of Contents

divider-byrayray.png

What is TypeScript?

In plain English, I would explain it as a Type system layer on top of JavaScript which compiles cross-browser JavaScript. On the TypeScript website, they explain it like this.

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

For people with experience in languages like Java, #C, and several others, are familiar with types. Those types will help users make our JavaScript more predictable.

Because there are already so many libraries build with TypeScript, or have support with TypeScript our IDE can help us with our JavaScript.

For example, it will tell us what kind of methods or properties a certain Object has. In the case of a function, it will tell what kind of parameters the function takes and the return type.

divider-byrayray.png

TypeScript Basics

You probably know that TypeScript is solving a bunch of problems we all have experienced in our time as a developer with JavaScript. Using string methods on numbers, trying to access properties that are not available in a certain object.

If you donā€™t see the point of learning TypeScript, read this post about why it is a good choice to start learning TypeScript right now, hope you like it.

Learning By Building

Letā€™s build a fictive pizzeria shop. (no we are not gonna build a real one) By building this you are going to learn all the basics of TypeScript. If you have experience with backend programming languages, some of the terms will be familiar for you, thatā€™s great!

If you donā€™t have any experience with any backend programming languages, donā€™t be afraid, I will teach you everything. And I know for sure you can learn it. Iā€™m also a very visual person, when I first started with TypeScript it was difficult, but along the way, I started to fall in love with it. Just give yourself some time and practice.

In my example, I would like to give a very practical way to start using TypeScript right away instead of only studying the theory so you have to implement it yourself. I like the practicality!

All my code can be found in my CodeSandbox envoirment which I created for this tutorial.

divider-byrayray.png

Set up your workspace

We need a few things on our computer to use TypeScript.

NodeJS

Install NodeJS via the NodeJS website or install it via Homebrew or NVM if you are on a mac.

TypeScript

npm install -g typescript

This will install TypeScript globally. After installation, it will be available via the tsc command.

Editor of Choice

I pick Visual Studio Code because it has TypeScript integrated.

Or if you just want to fiddle around with TypeScript, I would recommend using CodeSandbox with their TypeScript starter which helps you start right now.

The TypeScript website has a great playground to play around with TypeScript so you could see how the JavaScript looks that will be compiled.

Now we are good to go!

Primitives

Hopefully, you know all the primitive data and structure types in JavaScript.

  • String 'string'
  • Number 785
  • Boolean true
  • Undefined undefined
  • Null null
  • Object {}
  • Function function fake() {...}

If you donā€™t know them, I highly recommend you start learning them first. They are essential for using JavaScript and TypeScript. Check the MDN web docs section about data types and data structures.

In TypeScript, we use these primitive values in an interface to form a blueprint for an Object or Class like in the example below.

    // IPizza with required properties
    interface IPizza {
        name: string;
        slices: number;
        toppigs: string;
        price: number;
        cheescrust: boolean;
    }

Interface

Most of the models in TypeScript are a combination of interfaces and classes. An interface is a blueprint of a class or object. In this IPizza interface, we define all the properties a pizza has. In each property, we define what kind of data type the information is.

Every property that is defined in an interface is required. If you want to make it optional you have to use the ?. For example propertyName?: string if we define this property in an interface, it's optional. TypeScript won't give you an error if the property is missing in an object. On the other hand, if a property is required, it will give an error if the property is missing.

When a property is not defined in an interface you will get an error from the TypeScript compiler because the data is not according to the blueprint.

Example

We can all come up with properties for a pizza.

  • Name
  • Slices (the number of slices)
  • Toppings
  • Price
  • Cheesecrust
  • Vegan
  • Vegetarian

Letā€™s put them in the interface and decide what kind of data type they are.

    // IPizza with required and optional properties
    interface IPizza {
        name: string;
        slices: number;
        toppigs: string;
        price: number;
        cheescrust: boolean;
        vegan?: boolean;
        vegaterian?: boolean;
    }

In the example above we see an interface for our Pizza. We gave all the properties a single data type. Now we can create our Pizza object and use the interface to make sure it has the correct properties.

    const pizza: IPizza {
        name: 'Pizza BBQ',
        slices: 6,
        toppigs: 'Tomatosauce, BBQ sauce',
        price: 15,
        cheescrust: true
    }

Now the pizza is according to the interface. The interface is now a form of data validation. If we would add properties that are not in the interface or property with wrong data types, the TypeScript will give errors.

    const pizza: IPizza {
        name: 'Pizza BBQ',
        slices: 6,
        toppigs: ['Tomatosauce', 'BBQ sauce'],
        price: 15,
        cheescrust: true,
        meat: true
    }

With this object, you will get errors!

Multiple values

But what if we want to have an array of strings or numbers to give our toppings or sizes. We can do that pretty easy, just write string[] or number[] in the interface.

    // IPizza properties with an array of values.
    interface IPizza {
        name: string;
        slices: number;
        toppigs: string[];
        price: number;
        cheescrust: boolean;
        sizes: number[];
        vegan?: boolean;
        vegaterian?: boolean;
    }

Now our pizza object is valid.

    const pizza: IPizza {
        name: 'Pizza BBQ',
        slices: 6,
        toppigs: ['Tomatosauce', 'BBQ sauce'],
        price: 15,
        cheescrust: true,
        meat: true,
        sizes: [0, 1, 2, 3, 4]
    }

If we want to make a type an Array with multiple pizza objects, we can do that the same way with IPizza[].

const pizzaArray: IPizza[] = []

Enums

In the IPizza we have set that the value of toppings needs to be an Array of strings string[]. But we can do that a lot smarter because we donā€™t have tons of toppings for our Pizza. (We can apply this also for the sizes)

Letā€™s say we have 4 types of toppings and 5 sizes for our pizzas. We can define an enum for that. The first option that is defined in the enum will have value 0 by default. But you can set other values if you like.

    enum PizzaToppings {
        TOMATO, // value = 0
        BBQ, // value = 1
        NONE, // value = 2
        CREAM // value = 3
    }

    enum PizzaSizes {
        S = 's', // value = 's'
        M = 'm', // value = 'm'
        L = 'l', // value = 'l'
        XL = 'xl', // value = 'xl'
        XXL = 'xxl' // value = 'xxl'
    }

In the interface we add this to our properties. With the enums, we can have multiple choices for the sizes and toppings.

    // IPizza properties with an enum.
    interface IPizza {
        name: string;
        slices: number;
        toppigs: PizzaToppings[];
        price: number;
        cheescrust: boolean;
        sizes: PizzaSizes[];
        vegan?: boolean;
        vegaterian?: boolean;
    }

So our object will look like this.

   const pizza: IPizza {
        name: 'Pizza BBQ',
        slices: 6,
        toppigs: [PizzaToppings.TOMATO, PizzaToppings.BBQ],
        price: 15,
        cheescrust: true,
        meat: true,
        sizes: [PizzaSizes.S, PizzaSizes.M, PizzaSizes.L, PizzaSizes.XL]
    }

Classes

Instead of creating a normal interface, we can use a class, but we use the interface in the constructorto validate the data we put in the class.

Classes are handy because you can give your class methods, getters and setter which are not possible in an interface.

An interface won't be compiled into your JavaScript files, a class will be, because it's a valid data structure.

Letā€™s create a class based on our IPizza interface.

    class Pizza {
        name: string = '';
        slices: number = 8;
        toppigs: PizzaToppings[] = [];
        price: number = 0;
        cheescrust: boolean = false;
        sizes: PizzaSizes[] = [];
        vegan?: boolean = false;
      vegaterian?: boolean = false;

      constructor(data: IPizza) {
          this.name = data.name;
          this.slices = data.slices;
          this.toppigs = data.toppigs;
          this.price = data.price;
          this.cheescrust = data.cheescrust;
          this.sizes = data.sizes;

                if(data.vegan) {
              this.vegan = data.vegan;
                }
                if(data.vegaterian) {
              this.vegaterian = data.vegaterian;
                }
      }
    }

As you probably know, a class is great for making new instances of a special type of object.

    const pizza: IPizza = {
        name: 'Pizza BBQ',
        slices: 6,
        toppigs: [PizzaToppings.TOMATO, PizzaToppings.BBQ],
        price: 15,
        cheescrust: true,
        meat: true,
        sizes: [PizzaSizes.S, PizzaSizes.M, PizzaSizes.L, PizzaSizes.XL]
    }

    const bbqPizza = new Pizza(pizza)

If you would check your console, you will see that this bbqPizza is from type Pizza and not directly from type object. Off-course this is an object under the hood!

Array

But a pizza store with just one pizza is not enough right. Letā€™s make a big catalog of pizzas.

    class PizzaCatalog {
      list: Pizza[] = [];

      constructor(list: Pizza[]) {
        this.list = list;
      }
    }

Pizza[] will tell TypeScript that the property list gonna be an Array. Everywhere you put a [] after, will till it's gonna be an Array. Like string[], number[]or Pizza[], pick any type and it will work.

Now we can make a list of pizzas with the PizzaCatalog class.

const pizzaCatalog = new PizzaCatalog([bbqPizza]);

When you put it in a console.log it will output this.

    [Pizza]
        ā–¶0: Pizza
             name: "Pizza BBQ"
             slices: 6
            ā–¶toppigs: Array[2]
             price: 15
             cheescrust: true
            ā–¶sizes: Array[4]
             vegan: true
             vegaterian: false
            ā–¶<constructor>: "Pizza"

We can add even more pizzas to it.

    const hawaiPizza = new Pizza({
      name: "Hawai",
      slices: 6,
      toppigs: [PizzaToppings.TOMATO],
      price: 12,
      cheescrust: true,
      sizes: [
        PizzaSizes.S,
        PizzaSizes.M,
        PizzaSizes.L,
        PizzaSizes.XL,
        PizzaSizes.XXL
      ]
    });

    const vegiPizza = new Pizza({
      name: "Veggi",
      slices: 6,
      toppigs: [PizzaToppings.TOMATO],
      price: 11,
      cheescrust: false,
      vegan: true,
      vegaterian: true,
      sizes: [
        PizzaSizes.S,
        PizzaSizes.M,
        PizzaSizes.L,
        PizzaSizes.XL,
        PizzaSizes.XXL
      ]
    });

    const pizzaCatalog = new PizzaCatalog([bbqPizza, hawaiPizza, vegiPizza]);

The result will be.

    [Pizza, Pizza, Pizza]
            ā–¶0: Pizza
                name: "Pizza BBQ"
                slices: 6
                ā–¶toppigs: Array[2]
                price: 15
                cheescrust: true
                ā–¶sizes: Array[4]
                vegan: true
                vegaterian: false
                ā–¶<constructor>: "Pizza"
            ā–¶1: Pizza
                name: "Hawai"
                slices: 6
                ā–¶toppigs: Array[1]
                price: 12
                cheescrust: true
                ā–¶sizes: Array[5]
                vegan: false
                vegaterian: false
                ā–¶<constructor>: "Pizza"
            ā–¶2: Pizza
                name: "Veggi"
                slices: 6
                ā–¶toppigs: Array[1]
                price: 11
                cheescrust: false
                ā–¶sizes: Array[5]
                vegan: true
                vegaterian: true
                ā–¶<constructor>: "Pizza"

Perfect to loop through and build a cool webshop around it.

Loop through enums

To get our pizza toppings and sizes in a more readable way, we have to map through enums.

    // PizzaSizes enum
    export enum PizzaSizes {
      S,
      M,
      L,
      XL,
      XXL
    }

    export const PizzaSizeNames: string[] = Object.keys(PizzaSizes)
      .map(x => {
        if (new RegExp(/[0-9]/g).test(x)) {
          return PizzaSizes[x].toLowerCase();
        }
      })
      .filter(x => x !== undefined);

    export enum PizzaToppings {
      TOMATO, // value = 0
      BBQ, // value = 1
      NONE, // value = 2
      CREAM // value = 3
    }

    export const PizzaToppingNames: string[] = Object.keys(PizzaToppings)
      .map(x => {
        if (new RegExp(/[0-9]/g).test(x)) {
          return PizzaToppings[x].toLowerCase();
        }
      })
      .filter(x => x !== undefined);

To explain what it does. First we map through all the keys of the enum, then we check if a key is a number, because enum values are numbers by default. We set the enum string to lowercase and then we filter out all the undefined values.

The output from those variables is this.

    // PizzaToppingNames
    ["tomato", "bbq", "none", "cream"]

    // PizzaSizeNames
    ["s", "m", "l", "xl", "xxl"]

Functions

Now that we have our Pizza and PizzaCatalog classes, it's time to add a function to calculate the prices of the pizza based on the size. In the function we loop through all the sizes, calculate (in this case a random) an addition and sum it up with the price property value.

 interface IPizzaPrice {
      size: string;
      price: number;
    }

    interface IPizza {
      name: string;
      slices: number;
      toppigs: PizzaToppings[];
      price: number;
      cheescrust: boolean;
      sizes: PizzaSizes[];
      vegan?: boolean;
      vegaterian?: boolean;
      prices?: IPizzaPrice[];
    }

    class Pizza {
      name: string = "";
      slices: number = 8;
      toppigs: PizzaToppings[] = [];
      price: number = 0;
      cheescrust: boolean = false;
      sizes: PizzaSizes[] = [];
      vegan?: boolean = false;
      vegaterian?: boolean = false;
      prices: IPizzaPrice[] = null;

      constructor(data: IPizza) {
        this.name = data.name;
        this.slices = data.slices;
        this.toppigs = data.toppigs;
        this.price = data.price;
        this.cheescrust = data.cheescrust;
        this.sizes = data.sizes;
        this.prices = this.getPizzaPrices();
        if (data.vegan) {
          this.vegan = data.vegan;
        }
        if (data.vegaterian) {
          this.vegaterian = data.vegaterian;
        }
        return this;
      }

      private getPizzaPrices(): IPizzaPrice[] {
        return this.sizes.map((item, index) => {
          const addition = (this.price / 100) * 15 * index;
          console.log(this.price + addition);
          return {
            size: PizzaSizes[item],
            price: this.price + addition
          };
        });
      }
    }

For each pizza that is created with this class, you will get an array of prices because we calculate them in the constructor and add them to the prices property.

In this case, we have defined the getPizzaPrices method to output IPizzaPrice[] as an array. But if this would have been a function that would not return a value, we should have typed it with void like this example.

    function exampleFunction(): void {
        // we do a lot of things here ;-)
    }

Any

When I got started with TypeScript when the Angular team just launched Angular 2.0 I didnā€™t understand the benefit of TypeScript at all. So when I got errors, I just typed everything with any šŸ™ˆ which was bad!

The type any could be any type you want. This can be handy if you have to deal with a lot of generic types where you don't know what the type could be or it could be of any type.

Null and undefined

Hopefully, you know the difference between null and undefined. Because null could be simply explained with an empty value. undefined is not defined and in the case of an object property you can say the value of the property is not defined.

In TypeScript the behavior is the same as in normal JavaScript. But you can type property in a class with a type and say it has a default value that is null or undefined. Like we do in the Pizza class.

The weird thing with null is that if you check with typeof it will appear as an object, which can be weird.

    class Pizza {
      name: string = "";
      slices: number = 8;
      toppigs: PizzaToppings[] = [];
      price: number = 0;
      cheescrust: boolean = false;
      sizes: PizzaSizes[] = [];
      vegan?: boolean = false;
      vegaterian?: boolean = false;
      prices: IPizzaPrice[] = null;
        //....
    }

Thatā€™s a wrap. Now it is time for you to build cool stuff with TypeScript! If you want to share your projects in the comments, please do šŸ˜‰.

divider-byrayray.png

Conclusion

I hope it all becomes clear to use TypeScript. How it works and why itā€™s a great addition to your developer toolbox.

If you want to know why it is a good idea to learn TypeScript, check out my other post ā€œDoes Learning TypeScript Today Make Sense?ā€

divider-byrayray.png

Thanks!

hashnode-footer.png I hope you learned something new or are inspired to create something new after reading this story! šŸ¤— If so, consider subscribing via email (scroll to the top of this page) or follow me here on Hashnode.

Did you know that you can create a Developer blog like this one, yourself? It's entirely for free. šŸ‘šŸ’°šŸŽ‰šŸ„³šŸ”„

If I left you with questions or something to say as a response, scroll down and type me a message. Please send me a DM on Twitter @DevByRayRay when you want to keep it private. My DM's are always open šŸ˜

Did you find this article valuable?

Support Dev By RayRay by becoming a sponsor. Any amount is appreciated!

Ā