TypeScript 101 - Introduction

March 29, 2023

    In this post I intend to give a brief introduction to TypeScript, what it is and why it is useful. This is the first post in a series of posts I will be writing about TypeScript and is aimed at developers who are completely new to TS.

    What is TypeScript

    TypeScript is an open source programming language created by Microsoft which allows developers to add types. TypeScript is not able to run in browsers and instead it is compiled into regular old JavaScript, any valid JS is also valid in TypeScript meaning we can convert JS codebases to typescript in increments.

    Unlike many languages JS is what is known as a loosely typed language, (the opposite of this is a strongly typed language), this means we don’t have to specify specific types for the data we pass around our code. Examples of types are strings, numbers, arrays, booleans.

    Why is TypeScript useful?

    TypeScript is useful as it helps us write more robust code, and gives us powerful debugging tools in our IDEs. Imagine we want to write a simple addition function, we want it to accept two parameters a,b add these together and multiply the result by three.

    function addTwoNumbersAndMultiplyByThree(a, b) {
        return (a + b) * 3;
    }
    
    addTwoNumbersAndMultiplyByThree(2, 3)    // 15
    addTwoNumbersAndMultiplyByThree(2, "3")  // 69

    We can pass it the values 2 & 3 it works as expected and returns 15, fantastic. Now let's imagine we pass it 2 & “3”, easily done, we could have got the value from an input field as a string and passed it straight to the function, we now get a return value of 69. It returns us a number as expected, but we know this value is not correct. Now you might be able to spot straight away that 69 is not a correct value, but imagine we passed in 34 & “3394”, would the error be as obvious?

    This is where TypeScript comes in handy, let’s rewrite the code and specify that our function must receive values of the type number

    function addTwoNumbersAndMultiplyByThree(a: number, b: number) {
        return (a + b) * 3;
    }
    
    addTwoNumbersAndMultiplyByThree(2, 3)    // 15
    addTwoNumbersAndMultiplyByThree(2, "3") // IDE highlighting this function does not accept a string and the code will not compile.

    We can now see in our IDE that we are getting an error when we try to call our function by passing a string value rather than a number, we’re catching a potential bug nice and early before it gets into our production code base and causes an issue.

    Another common example of TypeScript’s usefulness is when trying to get values from an object that may not exist. For example, I’ve recently been working with an e-commerce platform built in React, as part of the code there in an object that is created to contain product details:

    const testProductOne = {
        name: "foo",
        sku: "sku123",
        price: {
            value: 15,
            currency: "GBP"
        }
    }

    However not every product has a value set for every attribute that exists, for example not every product has a sale price. If I try and access the sale price further in my code without checking it exists I will get an error at run time.

    function createPriceSavingsString(productDetails) {
        return `You are saving ${productDetails.price.value - productDetails.salePrice.value}`;
    }
    
    
    const testProductOne = {
        name: "foo",
        sku: "sku123",
        price: {
            value: 15,
            currency: "GBP"
        },
        salePrice: {
            value: 12,
            currency: "GBP"
        }
    }
    
    const testProductTwo = {
        name: "bar",
        sku: "sku456",
        price: {
            value: 20,
            currency: "GBP"
        }
    }
    
    createPriceSavingsString(testProductOne); // "You are saving 3"
    createPriceSavingsString(testProductTwo); // TypeError: productDetails.salePrice is undefined

    We can see in the above example that the first product detail function will return the string as expected but for the second where no sale price is present we will get an error message as we are trying to access a property on an object that does not exist.

    If I rewrite the above function using TypeScript and define sale price as optional using the ? symbol after the parameter name we can now see that our IDE highlights the potential error of productDetails.salePrice being undefined & our code will not compile.

    function generatePriceString(productDetails: {
        name: string,
        price: {
            value: number,
            currency: string
        },
        salePrice?: {
            value: number,
            currency: string
        }
    }): string {
        return `You are saving ${productDetails.price.value - productDetails.salePrice.value}` // 'productDetails.salePrice' is possibly 'undefined'.
    }

    We can now add in some logic to check whether salePrice exists:

    function generatePriceString(productDetails: {
        name: string,
        price: {
            value: number,
            currency: string
        },
        salePrice?: {
            value: number,
            currency: string
        }
    }): string {
        return productDetails.salePrice ? `You are saving ${productDetails.price.value - productDetails.salePrice.value}` : "You are paying full price";
    }
    
    // No compilations in above code as we are checking if salePrice exists in productDetails object

    And we have caught and eliminated a potential bug before our code has even ran.

    The above example is a little messy by passing the large type object into the function so TypeScripts allows us to define our types similarly to defining variables using the following syntax:

    // an object of the type ProductDetails expects to have a name property of type string, a price property of type Price and potentially a salePrice property of type Price
    type ProductDetails = {
        name: string,
        price: Price
        salePrice?: Price
    }
    // an object of the type Price expects to have a value property of type number and a currency property of type string
    type Price = {
        value: number,
        currency: string
    }
    
    // generatePriceString function accepts an parameter of type ProductDetails
    function generatePriceString(productDetails: ProductDetails): string {
        return productDetails.salePrice ? `You are saving ${productDetails.price.value - productDetails.salePrice.value}` : "You are paying full price";
    }

    Summary

    Link to TypeScript Playground with full example code and error highlighting

    There are many more examples of how TypeScript can help us write more robust code, but hopefully the above examples give you an idea of how it can be helpful, I intend to dive more deeply in some of the more advanced features of TypeScript in future posts, so please make sure to check back in the future!


    Peter Ford - UK Based front end developer specialising in React & Adobe Commerce / Magento. View my work on GitHub