• Social

An overview of TypeScript

TypeScript is a tool used to help improve the maintainability of your JavaScript code by allowing you to specify the type of data that should be used in your variables and functions, and then highlighting the times when when you have gone astray. Using TypeScript during your development, helps find and prevent bugs and also makes code more readable and descriptive during your development.

The first feature it includes is Static Type Checking. What this means is that you can specify the Type of information you expect in your functions, e.g. a string. If you were to add a number, and TypeScript was expecting a String, it would throw you an error. TypeScript also gives us Classes to work with. This isn’t as big a deal now what with ES6 having them built in, but before ES6, it was a big deal.

In the same way that Sass needs to be compiled to CSS, TypeScript needs to be compiled to JavaScript. This can be done using Node and the package TypeScript Compiler (shock!). The official documents provide a great introduction into setting it up for you to follow along with the below. It’s worth mentioning at this point that you should have good knowledge in JavaScript and JavaScript classes to fully follow along with the below (I wouldn’t want people reading this and getting frustrated if they didn’t pick it up!)

Defining Data Types in variables

The first feature of TypeScript is that it allows you to define the type of data a variable should be. The below code highlights the different types you can use and if you run the below code with your javascript compiler  you will see that it does not flag any issues because the defined types have all been adhered too. You will see you can require variables to be strings, numbers, booleans, arrays, number array, mixed arrays (using tuple), undefined or any of the above (any).

let myString: string
let myNum: number
let myBool: boolean
let anyVar: any

// array of strings, array of numbers, array of booleans.
let strArr: string[] // has to be an array of strings
let numArr: number[]
let boolArr: boolean[]

// Note you can also write the above as follows but I prefer the above
// let sttArry: Array<string>

// Tuple means it must match the given array
let strNumTuple: [string, number]

// Void 


myString = "Hello" + " World"
myNum = 2.8
myBool = true
anyVar = "Whatever type you want"

strArr = ['first string', 'second string']
numArr = [1, 2, 3]
boolArr = [true, false, true]
strNumTuple = ['Hello', 1] // ['Hello, 1, 2, 3] would also work as only need to pass initial checks.


console.log(myString, myNum, myBool, anyVar, strArr, numArr, boolArr, strNumTuple)

Defining Data Types in functions

The next useful feature of TypeScript are it’s use with functions. The below code demonstrates how you can define the type of data you want a function parameter to be, and also the type of data you want it to return. Note in the below code the use of the question mark which means that the parameter is optional. Although, it’s not strictly TypeScript the function, it’s also good practice to cover different types of data used in the parameters. In the example, we test for the typeOf data and then act accordingly.

// the arguments have to be numbers as does the return value
function getSum(num1: number, num2: number): number {
    return num1 + num2
}

// console.log(getSum(1, 3))

// the below code allows for someone to add a string or number!
let getSum2 = function (num1: any, num2: any): number {
    if (typeof num1 == 'string') {
        num1 = parseInt(num1);
    }
    if (typeof num2 == 'string') {
        num2 = parseInt(num2);
    }
    return num1 + num2;
}

// getSum2('2', 2)

function getName(firstName: string, lastName?: string): string {
    if (lastName == undefined) {
        return firstName;
    }
    return firstName + ' ' + lastName;
}

console.log(getName('John', 'Doe')) // The question mark means lastName is optional!

Interfaces

In the same way we can write let myString: string; we can also use something called an interface, which is essentially the allowed data types of the key values. The below example should help clarify things, whereby you are specifying the showTodo function that the parameter must be an object with a title and text key that should both be strings.

interface Todo {
    title: string,
    text: string
}

function showTodo(todo: Todo) {
    console.log(todo.title + ": " + todo.text)
}
let myTodo = { title: "trash", text: "take out trash" }

showTodo(myTodo)

Classes

These are a feature that are now baked into ES6 and so if you’ve used ES6 classes… well then these are nothing new! The code below starts by defining an interface, which sets out the keys and methods that the class must have. We then create a new class called User which implements the userInterface we have just specified. The User class, starts by defining all the keys that the class can take. Notice the public, private and protected specifications which will change the closure of the keys in children classes.

The constructor function is baked into classes and is run when a new User class is created. It sets the keys to the parameters that are handed to it.  We can then use this class by creating a new user called Tom with all the relevant data.

We then want to add member functionality to our software, whereby users can become members. To do this, we can use a feature of classes called extends which means that the Member class will inherit all of the methods and keys of the parent User class but allow us to add new methods and keys on top. We add a new key called ID, because in our imaginary system, Members need an ID but Users don’t. You will notice in the Member class, we can use the super function, another feature of classes, which means it will use the parent class keys (name, email, age). The Member class also needs a payInvoice function and so we can use the parents method by calling it with super.

interface UserInterface {
    name: string,
    email: string,
    age: number,
    register(),
    payInvoice()
}

class User implements UserInterface {

    name: string;
    age: number;
    email: string; // can't access from outside the class
    public height: number; // can access from outside the class 
    protected address: string; // can access if the class inherits from this User Class (e.g. class SuperUser extends User)
    private notes: string


    constructor(name: string, email: string, age: number) {
        this.name = name
        this.email = email
        this.age = age

        console.log('user created: ' + this.name)
    }

    register() {
        console.log(this.name + ' is now registered')
    }
    payInvoice() {
        console.log(this.name + ' has paid his invoice')
    }
}

// this will throw a notification in typescript and say that age is protected
let Tom = new User('Tom', 'hello@tomhoadley.co.uk', 28)

console.log(Tom.age)

// extending the user class
class Member extends User {
    id: number

    constructor(id: number, name: string, email: string, age: number) {
        // need to call super on children classes.
        super(name, email, age)
        this.id = id
    }

    payInvoice() {
        super.payInvoice()
    }
}

let bob: User = new Member(1, 'Bob Smith', "bob@gmail.com", 22)

bob.payInvoice()

Although the above is a slight divergence into classes rather than TypeScript, it is helpful to see how classes and TypeScript can work together to define the types of data that your code requires.

Again, this is useful for the maintainability of good software as it flags errors clearly as you build your code.

Scroll Up