TypeScript: Type Annotation & Type Inference

Iain Robertson
5 min readApr 5, 2021

TypeScript is a framework for JavaScript that implements a type system in your JavaScript code. It analyzes the code we write using type annotations, with the intent of catching errors during development. It is only active during development, and transcompiles into JavaScript. What this means for us as developers, is that typescript proofreads our code as we write it, limiting our time chasing bugs and tracking down errors during development, expediting the process. Without Type systems such as TypeScript, the only way to catch an error in traditional JavaScript is to run your code, and see the error after the fact.

An essential concept to grasp when initially learning TypeScript is type annotation. Type annotation is, in essence, a system by which we can define the type(s) associated with a variable, function, or object. This definition provides TypeScript with the information it needs to monitor our code, and catch errors during development. If we define a variable as a string upon initialization, TypeScript will alert us should any other type occupy that variable during the course of development. For Instance:

Here we have used type annotation (the colon and the term “string”) to define the variable “moon” as a string, so when we attempt to reassign its value, TypeScript underlines “moon” and alerts us that the variable type has changed from and string to an integer. This saves us the trouble of discovering this error when our code is run. However, TypeScript is a clever framework, and it has the capability to infer the variable type based upon context, provided the variable is initialized and assigned a value on the same line. This, rather sensibly, is known as Type Inference, and can be seen in action here:

As before, we initialize our variable moon and assign its value as a string within our TypeScript file, and TypeScript is clever enough to deduce that moon must be a type of string and any other type assigned to it would be an error and break the code. This Type Inference spans the majority of type assignments, which raises the question, when do we explicitly use type annotation?

  1. When a variable is declared on one line, but initialized on another

TypeScript is clever, but the scope of its abilities is very specific. In order to infer the correct type, the variable must be declared and initialized on the same line. If we declare a variable without initializing it (assigning a value), TypeScript will assign it the “any” type:

This is far from what we want. We want to avoid the “any” type as often as possible. It limits TypeScript’s ability to monitor our variable and catch errors. In an instance like this, we need to use type annotation.

With the above code snippet, TypeScript has now been explicitly told what type our variable should be, and will report any errors associated with that variable.

2. When using a function that returns the “any” type.

When writing code in the context of a TypeScript file, there are certain built in functions that will return a value of indeterminate type. Since the possibilities are so vast, and TypeScript prefers to infer rather than imply, it will assign the “any” type to be safe. It is therefore our responsibility to use type annotation to alert TypeScript as to what type it should expect as the output of that function. One such built in function is JSON.parse()

In this instance, if we do not define the type of data expected from our JSON object, {age: number; height: number}, TypeScript will not be able to catch errors for our data variable.

3. Variables whose type cannot be inferred correctly.

In certain instances, a variable may need to be able to change type by design. If we have a variable that is defined and initialized as a boolean, but will later need to be an integer, we must use type annotation to explicitly tell TypeScript to expect two possible types for that single variable:

Here, TypeScript is doing its job, inferring that myFavoriteNumber is a boolean, and any other type assigned to it, such as an Integer, must be an error. But in this instance, I want myFavoriteNumber to be either a boolean or an integer, and to accomplish this, I must use type annotation:

By using a pipe (|), I am able to inform TypeScript that this variable should be considered valid as either a boolean or number statement.

The ability of TypeScript to infer types and actively monitor our code for errors is incredibly powerful, and does a tremendous job of streamlining our workflow, but it is not perfect. There are circumstances where we have to use type annotations to define our expectations of variable types to TypeScript to better serve our app, but the where and when to use these annotations should be increasingly easier to navigate if you follow the above guidelines.

--

--