Typescript’s Type System is an essential tool for building complex applications using Typescript. It is one of the core strengths of Typescript as a language. This type system allows Typescript to facilitate static type checking and sets it apart from Javascript.

In case you are interested, you can go through this detailed post about what makes Typescript different from Javascript.

Typescript provides a bunch of types (including basic data types and also complex types). In this post, we will take a deep-dive into the type system of Typescript.

1 – Typescript Types By Inference

Typescript is a super-set of Javascript. Therefore, it can generate types for you even when you are actually writing Javascript code.

For example, when you create a variable and assign a particular value to it, Typescript will automatically decipher the type.

let greeting = "Hello, World from Typescript";
greeting = 10

In this case, Typescript is intelligent enough to understand that the variable greeting stores a value of type string. As a result, the second statement will show a compilation error.

2 – Typescript Primitive Types

Just like all programming languages, Typescript also provides three primitive types. Let us check them out one-by-one.

  • The string type represents string value such as “Hello, World from Typescript” or “The Way of Kings”.
  • Next, we have the type number. This is for numbers such as 16, 81 and so on.
  • Lastly, we have boolean. This is used for two values – true and false.

The three primitive types forms the backbone of Typescript’s type system.

3 – Type Annotations for Variables and Functions

We can use the primitive types for any variables. While it is optional, providing an annotation can be considered a good practice. It definitely increases code readability.

let greeting: string = "Hello, how are you?"

If no type annotation is present, Typescript automatically tries to infer the type. This is done on the basis of the value.

We can also use type annotations with functions.

Function are the building blocks of any program. It is important that they are well documented in terms of input and output parameters.

When we declare a function, we can add type annotations after every parameter. See below example:

function saveBook(title: string) {
  console.log("Book Saved: " + title.toUpperCase() + "!!");
}
saveBook(25);

This will give an error during compilation. Basically, the valid type of title property is string. However, in the function call we are passing a number as input.

We can also add annotations for the return type from the function. See below:

function getBookTitle() : string {
   return "The Way of Kings"
}

Even if we didn’t specify the return type, Typescript will still be able to infer the correct return type depending on the value. For documentation purposes, it might be a good idea to mention the type explicitly.

4 – Typescript Types for Anonymous Functions

Anonymous functions behave differently in terms of function declarations. Based on how the function is called, Typescript can determine the parameter types even in the case of anonymous functions.

See below example:

const books = ["The Way of Kings", "The Eye of the World", "The Dragon Reborn"]

books.forEach(function (book) {
    console.log(book.toUppercase())
})

books.forEach((book) => {
    console.log(book.toUppercase())
})

In both the cases, Typescript will report an error. By looking at the type of array (containing all strings), it will infer the type of the variable book as string. Since the function name is toUpperCase() and not toUppercase() for string data types, it will throw a compilation error.

This is known as contextual typing. Basically, the context of the function determines the type of value.

5 – Typescript Object Types

Primitives are fine for smaller applications. But for complex applications, we often need to use complex data types such as objects. Basically, objects are Javascript values with properties.

See below example where we use an object:

function printBook(book: {title: string; author: string}) {
    console.log("Title: " + book.title);
    console.log("Author: " + book.author);
}

printBook({ title: "Way of Kings", author: "Brandon Sanderson" })

In the above example, we annotate the parameter book with a type having two properties – title and author. Both are of type string.

We can also declare optional properties as below:

function printBook(book: {title: string; author?: string}) {
    console.log("Title: " + book.title);
    if (book.author !== undefined) {
        console.log("Author: " + book.author);
    }
}

printBook({ title: "Way of Kings", author: "Brandon Sanderson" })
printBook({ title: "Eye of the World" })

To declare a property as optional, we add a ? after the property name. However, when we try to access it, we have to check for undefined. This is because Javascript does not give any runtime error in this case. And Typescript follows the Javascript runtime behaviour.

6 – Type Composition in Typescript

Using Typescript’s type system, we can also build new types using existing types. This is mainly done using operators.

Let us see examples of type composition.

6.1 – Defining Unions in Typescript

The first way we can combine types in Typescript is by using union type. A union type is formed from two or more types. Basically, the idea is that the value might belong to one of the types in the union. Each possible type is a member of the union.

See below example of a union type in action:

function printUserId(userId: number | string){
    console.log("User Id is: " + userId);
}

printUserId(11);
printUserId("Adam")
printUserId({id: 127})

Here, we have a property userId. As per the function definition, it can have a number value or a string value. Next, we have 3 calls to the function with different values. The first two calls having number as input and string as input are perfectly fine. However, the third call shows a compilation error. This is because the userId can only contain numeric or string values and not an object.

6.2 – Typescript Generics

Generics help make Typescript types adhere to certain rules. For example, a normal array in Typescript can contain any type of item. Each item can be of a different type. However, we can use generics to define arrays that contain items of a specific type.

See below example:

type StringArray = Array<string>

let books: StringArray = ["Book1", "Book2", 1]
Type 'number' is not assignable to type 'string'.ts(2322)

In this case, we have declared an array using generics that can only contain strings. This is the reason why the assignment statement shows an error when one of the items is a number.

Basically, generics can help us enforce better type-safety in Typescript.

7 – Typescript Enums

Enums is one of the Typescript features that is independent of Javascript.

Basically, we can use Enums to define a set of named constants. See below example:

enum Genre {
    Fantasy,
    SciFi,
    Thriller
}

let genre = Genre.Fantasy;

In this case, Genre is an enum with three possible values. If interested, check out this detailed guide on Typescript Enums.

8 – The Any Type

There is another special type available in Typescript known as any. We can basically use this type whenever we don’t want type-checking errors.

See below:

let confused: any;

confused.toUpperCase()
confused = 2
confused = "Hello"

The any type is an easy way out when we want to avoid the hassle of using a particular type for the sake of satisfying Typescript. However, it is important to know what we are doing when using any.

Also, when we don’t specify a type and Typescript cannot infer it, the compiler considers it as any. In this case, there will be no type checking. To avoid this, we need to use compiler option noImplicitAny to flag implicit any as error. To know more about setting compiler options, you can check out this post on Typescript TsConfig options.

Conclusion

Typescript’s type system is a great tool to write robust and error-free code. As a good practice, we should try and use it wherever possible.

It is also quite flexible when needed. We can use various options such as generics, unions to modify the behaviour of standard types.

If you have any comments or queries about this post, please feel free to mention them in the comments section below.

Categories: Typescript

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *