Typescript Part II: Typescript & the Types You Need To Know

Profile picture of Bhavik Bamania
Bhavik Bamania
·16 min read
Types in Typescript
Types in Typescript

If you recall the first part a bit then you must be aware of the fact that typescript is all about types that are used to make Javascript more optimized and efficient, in the article, we will walk through all the types provided by typescript to make the javascript coding disciplined.

After reading the description if you looking for the first part then you need not worry but you need to go through part — I of this series, if you can’t be able to find out where is this part 1 check it out here.

Get Started

Without wasting a second in gibberish chit-chat let’s dive into the ocean directly!

Using Types

Just like data types in any programming language, typescript also has a set of types to work with (actually, too many types) but as per the ritual, we will begin our show with the basic types ie., core types.

Core Types

  1. Number — Just like it sounds, it is used to work with number datatype, however, it is important to note here is just like Javascript, Typescript doesn’t differentiate between floats and integers, therefore, 1,7.3 or -10 all caters to one ie., Number.
  2. String — If you are aware of Javascript or any other programming language you don’t need any introduction here, am I right? Any textual or numerical value enclosed in a pair of double quotes is deemed as a string. For instance, “Hi”, and `hi`, ‘hi’ — are all considered as strings.
  3. Boolean — The Sun rises from the East is it true or false? Well, whatever the answer is, the point is that’s how Boolean works!

You must be wondering if these types are also available in Javascript then why on earth do we need typescript and additional dependency?

If I sum the answer in one line I’d say that typescript adds an extra layer, and extra sanity check in your code. Typescript only checks during compilation and works during development, ie., before the code is compiled, however, for better understanding let’s check the difference between Javascript types and the Typescript types, this will settle the debate once and for all!

Javascript Types vs Typescript Types

  1. Javascript types are dynamic types whereas Typescript types are static types.
  2. Dynamic types are the types that can be changed during runtime, whereas in static types, we need to define the types as per our needs.
  3. In the case of dynamic types, we don’t have control over them and thus leading to unexpected behavior, whereas in static types we define those types just to control the flow of our code.

IMP: In Typescript all the core primitive types are lowercase. Thus in Typescript, you will work with types like string or number NOT String or Number, etc.

So, now I guess you are aware of the core types in Typescript, but as you know with programming languages knowing something doesn’t work much unless you have a hands-on grip, and for that let’s have some code to demonstrate what we have understood.

function add(n1: number,n2:number, showResult: boolean, phrase:string) :number | undefined {
    let result = n1+n2;
    if(showResult) {
        console.log(`${phrase}${result}`)
    } else {
        return result;
    }
}

const number1 = 10;
const number2 = 7.8;
const printResult = true;
const resultPhrase = "Result is: "

add(number1, number2,printResult,resultPhrase);

As you can see here, in this example all the core types discussed above have been used however, if you have noticed you can able to see something “: number | undefined” what the hell is this? This is Union type in typescript and for the moment you can skip this part as we will discuss it in detail as we proceed further. For the moment all you need to know is that’s how you can write your first typescript code using all the core types.

Type Assignment & Type Inference

While working with typescript you will find these types often in use, and at that time, you will wonder what it means. Thus, let’s understand these two first before hitting our editor for demos!

Type Inference

Type inference means that the typescript itself identifies the type of the variable or constant by determining the type of the assigned value. You will find this often if you just hover over any variable name of the function in case you haven’t assigned any type to it.

Type Assignment

We can explicitly define the types while assigning the value during their declaration. For instance

let x: number = 5;

Here the x: number is what we call a Type assignment.

It’s important to note here that you need to use Type assignment when you are just declaring the variable or const, but not assigning any value to it. For example

let x: number;

Another important thing that we should know before moving further, is if we are using const and assigning the initial value to it, without specifying the type of that const, the typescript inference will assume the value of that const as it is the default value. For example,

const x= 5;

When you hover over this const x in your IDE it will show something like this which means this x will have 5 as its type

const x: 5;

Dealing with Objects

Typescript object types are written not as object key-value pairs, instead, they are written as key-type pairs. They are used to describe the type of objects that are being used somewhere.

When working with objects, it is essential to note that:

  1. In the case of an object, we need to be more specific, if we are not specific then Typescript will throw the error.
  2. TS doesn’t support any type of object because we haven’t given any specific information about the object.
const person : {
    name: string;
    age: number;
} = {
    name: "Pyramid Oscar",
    age: 28
}

console.log(`person name ${person.name}`)

// working with nested objects

const nestedObject : {
    id: string;
    price: number;
    tags: string[];
    details: {
        title: string;
        description: string;
    }
} = {
    id: "1",
    price: 200,
    tags: ["test"],
    details: {
        title: "Title",
        description: "description"
    }
}

The above example shows the basic usage of typescript when working with objects, however, this is not a very good practice for a beginner I don’t want things to be pretty complicated, therefore, let’s take this for the moment, and in the meantime we will learn more we can work on best practices.

Another important thing you have noticed here is we can work on the nested objects as well the thing we might need much more in real-time programming.

Working with Arrays

Just like other data types supported by Javascript, typescript supports working with an array as well, any type of Javascript array is supported be it a normal array or an array of objects. They can be of a single type or mixed types ie., consist of various data types like strings and numbers altogether.

The typescript provides type support to the array can be flexible or strict. Let’s see a basic implementation of types on an array.

const product : {
    id: string;
    price: number;
    tags: string[];
    details: {
        title: string;
        description: string;
    }
} = {
    id: "1",
    price: 200,
    tags: ["tag1, tag2"],
    details: {
        title: "Title",
        description: "description"
    }
}

for (const tag of product.tags) {
    console.log("operating string functions on the fly!", tag.toUpperCase());
}

In the above example, you can see, a weird thing string[] this weird thing tells that tags can have an array of strings of any length.

Working with Tuples

A tuple is a fixed-length array consisting of pre-defined types. It is a special construct that is understood by Typescript but not by Javascript. It will tell TS to have a special array with exactly the same elements mentioned in the key type.

const x:[number, string] = [2,”author”];

The above tuple will accept only two values one of number type and another one of string. It won’t allow you to add any other value apart from the number and the string won’t allow you to add the third value to it. It also keeps a check on the length of the array working as a tuple. It can be of the length of 2 only in the above example. It can be pretty useful when you know the type of data working and the type of data you are expecting.

Now as you are aware of tuples theoretically let’s have a quick demo and there we will see one exception to the tuples.

onst personDetails : {
    name: string;
    age: number;
    hobbies: string[];
    role: [number, string];
} = {
    name: "Pyramid Oscar",
    age: 28,
    hobbies: ["Writing", "Coding"],
    role: [1, "author"]
}

// in case of tuples unfortunately push is allowed.
personDetails.role.push("admin");

// personDetails.role[1] = 10 NOT ALLOWED!!!

// person.role = [] works well with length as well, the length should be equal to the specified length in a tuple.

If you have noticed the above example if we do an array.push() the typescript won’t yell at us and accepts it as a valid scenario, this is the exception to the tuples, also in the case of push() the length is not a constraint anymore as shown in the above example.

Working with Enums

It is a special class that represents a group of constants, it is only supported by Typescript. Enums ships with two varieties either string or numeric.

Values of these numbers can be any number, string, or mixed one as per your preference. Let’s give it a try to check how they work

enum Role {ADMIN=1, READ_ONLY=2, AUTHOR="AUTHOR"}
const personData = {
    name: "Pyramid Oscar",
    age: 28,
    hobbies: ['Coding', 'Writing'],
    role: Role.ADMIN,
};

for (const hobby of personData.hobbies) {
    console.log('hobby',hobby);
}

if(personData.role === Role.ADMIN) {
    console.log("is admin")
}

Again they are very useful when we have a list of constants with which we want to work.

The “any” type

Perhaps this is the type that every newbie would hear or learn about it as soon as he starts learning Typescript. It is the most flexible type provided by typescript, as it is used to store any kind of value and it doesn’t denote any specific type.

When learning typescript any type may seem to be a very good friend of yours as it rescues developers when they get stuck with typescript, but on the contrary, it is better to avoid this as much as you can as it will stripe all the advantages of Typescript and it will be just like working with Javascript.

const x: any;

t can be used only when you are not at all sure what kind of data you are going to get then use “any”.

Union Type

It can be helpful when you have to work with two different sets of types, for example, number & string as it allows you to assign multiple types to a param, and based on that it will allow you to return the type of result based on the input.

Let’s understand union with an example.

unction flexibleAdd(input1: number | string, input2: number | string) {
    let result;
    if(typeof input1 === "number" && typeof input2 === "number") {
        result = input1 + input2;
    } else {
        result = input1.toString() + input2.toString();
    }
    return result;
}

const additions = flexibleAdd(10,10);
console.log("addtions", additions);

const concateStrings = flexibleAdd("Pyramid", "Oscar");
console.log(concateStrings)

Here in the example, both input params expect the input can be a string or number and yes that’s the only thing union does!

Literal Type

Literals are based on the core types of the datatype but they have a specific version of that datatype. For example: const number1 = 2.8;

In the above example, if we hover over the number you will see that type inference is expecting it to be only 2.8.

It can be used when we have a set of hard-coded possible values or pre-defined values. They can be useful when they are used with Union types. Let’s understand this with an example.

function combinator(input1: number | string, input2: number | string, resultType: 'number' | 'text') {
    let result;
    if(typeof input1 === "number" && typeof input2 === "number" || resultType === "number") {
        result = +input1 + +input2;
    } else {
        result = input1.toString() + input2.toString();
    }
    return result;
}

const sum = combinator(10,10, "number");
console.log("addtions", additions);

const concateString = combinator("10", "10", "number");
console.log(concateStrings)

In the above example, check the parameter “resultType” as it expects the value “number” and based on that the function is executed. That’s one of the better uses of literals, in the above example typescript will yell at us if do a typo here while passing values to “resultType”.

Type aliases

Type aliases can be used to “create” your own types. It can serve as a better alternative or I would say a shorthand to the Union types, or to complex object types.

Let’s have an example to understand type aliases efficiently.

type Combinable = number | string;
type ResultType = 'number' | 'text';
type User = {name: string; age: number};

function combinable(input1: Combinable, input2: Combinable, resultType: ResultType) {
    let result;
    if(typeof input1 === "number" && typeof input2 === "number" || resultType === "number") {
        result = +input1 + +input2;
    } else {
        result = input1.toString() + input2.toString();
    }
    return result;
}

const combine = combinable(10,10, "number");
console.log("addtions", additions);

const combineString = combinable("10", "10", "number");
console.log(concateStrings)

// type aliases can be useful with object as well.

const user : User = { name: "Pyramid Oscar", age: 28}

Check the first two lines, type Combinable, and type ResultType are custom types which we have created to store our complex types in this case union types, we can do the same with Object was as you can see here in the example we have one object called user and we have defined its type as User which expects two properties, name, and age.

Functions and their types and working with void

Just like assigning types to a variable or constant, we can assign types to functions as well. We can do so based on the type of data the return statement is returning from a function.

function summingUp(number1: number, number2: number): number {
    return number1 + number2;
  }
  
  function displayResult(num: number): void {
    console.log("Result ", num);
  }
  
  displayResult(summingUp(5, 10)); // => outputs: 15

  console.log(displayResult(summingUp(5, 10))); // => return "undefined", because function that return nothing will be technically in JS returns undefined.

In the above example, the function summingUp is expected to return a number, while displayResult is expected to return nothing (that’s void).

If we don’t assign a type to the void function, typescript inference will automatically assign a void type to the function.

In such a case, if you console.log such function it will return undefined, but we can’t assign undefined to the function, because typescript will assume that a function should return something, and therefore if the function is not returning as anything typescript will consider it as void.

Difference between void and undefined types in Typescript?

If there is no return statement in the function typescript will consider it as void even though it will return undefined.

In such case, if you deliberately assign undefined it will yield an error because TS now expects that function should have a return statement.

However, the undefined type will be rarely used and you mostly will end up in cases where your function won’t return anything and will be considered void.

Functions as Types

In Javascript, we can point a function to a variable for example: let combineValues= add; where add is a function. Now, if we call combineValues() then it will execute the function that we have called which is add() but then there is a problem, that we can assign anything to a variable in Javascript and it won’t complain but when it comes to typescript it will yell at you when you try to do it.

So to fix this nasty situation, typescript has something called “function as types” and it proposes two types of solutions, one is a flexible one and another one is a more strict and neat solution. Let’s check both one by one.

let combineValues: Function = add;

In the above example, here now typescript knows that there is one variable combineValues and it is holding add(), for the moment we can assume it has two parameters a and,b and this add function returns the summation of these two values.

As a developer, we know that it will return a number as an output, but with the above example typescript only knows that it is just a function and it doesn’t know what type this function is returning. This leads to one more problem, to combineValues you can assign any function that is a problem.

Therefore to resolve this problem, typescript has a more strict and neat approach let’s see what it is.

let combineValues: (a: number, b:number) => number;

Now only those function who has two parameters of number type and a function that returns a number can be assigned to combineValues variable if we try to assign anything else typescript will throw the compilation error.

Based on whatever we understood let’s have a quick example for hands-on practice.

// Functions as types

function addingUp (a: number, b: number) {
    return a + b;
}
let combinedValues: (a: number, b: number) => number;
combinedValues = addingUp;

console.log(combinedValues(8, 8));

Function as Callbacks

During development, you may need to use callbacks, to a function. In Javascript, it is a bit easy because there is no typescript there to yell to you when you do anything. But when it comes to typescript, we need to be in discipline and then while working with callbacks let’s see how we can create a callback with typescript with an example.

// Function Types & callbacks

function addAndHandle(n1: number, n2: number, cb: (num: number) => void) {
  const result = n1 + n2;
  console.log(cb(result));
}

addAndHandle(10, 20, (result) => {
    console.log(result);
    return result; 
    
});

In the above example two things we need to consider,

  1. No type assignment on the result param because we have made it clear in the call back that callback will expect one param of number. Check line no 3 in the above example.
  2. This won’t be poked by typescript because, when we pass void as type we are telling typescript that we’ll ignore the result that might be returned by the function. This means the callback here has nothing to do with the return value. Check line no 3 in the above example.

The unknown Type

The unknown is a type in typescript that we used to use in cases where we are not sure what value we are getting or want to store. It may sound similar to “any” but there is a difference between these two.

In case of any, TS will give up its check and tell you to do whatever you want to do. But in case of an unknown, typescript will keep on checking whatever we are passing as value.

It means you can’t assign anything to an unknown type variable without checking its type.

let userInput: unknown;
let userName: string;

userInput=5;
userInput="Pyramid Oscar";

if(typeof userInput ==="string") {
    userName = userInput;
}

The never Type

It is used when your function intends to return anything and will crash the script for example throwing an error or in case of an infinite loop. Let’s take a look at this example

function generateError(message:string, code:number): never {
    throw{message:message, errorCode:code};
}

const result = generateError("Something went wrong",500);
console.log(result);

If we try to log the output of this function we will get nothing because it never returns anything not even undefined. After all, this function will break the script and end the script abruptly.

Conclusion

If you have reached here by following every single line and performing demos alongside then I can conclude one thing for sure ie., now you are well aware of the types in the typescript and now you are ready to move to the next step i.e., using these types with classes and learning a new concept called interfaces which is purely a typescript concept but we will cover all these in the next blog. If you want the source code of the demos we have done and the demos we will going to do you can hit here to fork or clone it.

Author of Bhavik Bamania

Written by

Bhavik Bamania