What is TypeScript and why would I use it in place of JavaScript? All You Need to Know to Get Started

Fortum Tech
Fortum Technology Blog
9 min readFeb 11, 2022

JavaScript is one of the most popular programming languages and that is for a good reason.

It is easy to get started with almost no setup. You can just open the browser console and start coding. JavaScript has a strong ecosystem that allows building reliable products fast and securely. From adding an animation to a website to creating large frontend and backend projects from scratch, there are almost no limits to what JavaScript can do in digital development.

But simplicity comes with its own cost.

JavaScript makes it easy to fall into a trap of producing unmaintainable code with unpredictable behavior. The main culprit is JavaScript dynamically typed characteristics, which means that types of variables don’t have to be explicitly specified in the code but will be inferred from the value during the run time. On the one hand, it allows developers to quickly write code without having to specify variable types, but on the other hand developers often find themselves searching for a nasty bug that occurs because of the lack of static typings that could have easily prevented that behavior.

One of the most common errors in JavaScript Cannot read a property of undefined“ happens due to lack of typing’s, where an object is expected to have a specific property, but JavaScript is unable to automatically find it and therefore, we end up with a broken code.

How can we prevent such types of errors? The answer is simple, add static types to our JavaScript code using TypeScript, a strongly typed programming language that builds on top of JavaScript.

What is TypeScript?

TypeScript is a new programming language released by Microsoft in October 2012 after two years of internal development and currently being adopted by many organizations worldwide.

One of the key reasons why Typescript grew quickly in popularity is because it acts as a superset of the already popular JavaScript language, expanding all its existing features with strong static typings.

Being a superset of JavaScript means that a valid JavaScript program is also a TypeScript program, either on the client or server side, making it easier for developers to get started with TypeScript.

Understanding How TypeScript Works

The importance and need for static typings in JavaScript is easy to understand to anyone who faces its challenges every day. To learn how to solve that challenge using TypeScript and learn more about its advantages, it’s important to understand how TypeScript works in practice.

It was mentioned that TypeScript compiles to JavaScript by the TypeScript compiler, but what is this compiler and what does the compilation process look like?

Everything starts with the tsconfig.json configuration file that must be placed in the project’s root folder. This file defines options that the compiler needs to use during the compilation process. The good thing is that you don’t need to specify most of the options by yourself. The GitHub tsconfig bases repository has great configuration examples for most of the environments. You can simply extend them in your tsconfig.json and focus on configuring values specific to your project.

The next step is to install the TypeScript compiler and compile your TypeScript code to JavaScript. Before doing that, you need to know whether you are going to install it locally or globally in your machine. Depending on the chosen way, the path to the compiler in package.json might need to be adjusted.

You can either install locally (adding to dev dependencies) using the command yarn add -D typescript or globally using the command yarn global add typescript

To compile TypeScript files to JavaScript, run the tsc command if you installed the compiler globally or $(npm bin)/tsc otherwise.

Important note: In the next chapters we will assume that TypeScript was installed globally. If you want to proceed with the local installation, make sure to create a new script in package.json file and use it by executing yarn build:

“scripts”: {
“build”: “tsc”,
},

Remember that package.json automatically loads the contents of the $(npm bin) folder, so it is not necessary to specify the full path to TypeScript compiler (i.e.: $(npm bin)/tsc), but simply tsc should be enough.

Creating a simple Typescript application

Don’t be overwhelmed by the number of available options in tsconfig.json, it’s always best to start with a simple example and learn the other options along the way.

Let’s create a simple TypeScript application and compile it to JavaScript!

The starting point for the compilation process is to create and define the tsconfig.json configuration file, but we don’t have to start from an empty file. The command tsc --init enables us to generate a configuration file with default values and lets the compiler automatically use it.

After executing the command in the root folder of the project, check the console output for the following message: “Successfully created a tsconfig.json file.” and confirm that a tsconfig.json file was created.

Open the tsconfig.json file and check its contents. There are several predefined values commented out, but we can also notice some that are active, so let’s figure out what they mean:

  • `compilerOptions` — the top-level configuration option that defines how a TypeScript compiler should behave
  • `target` — ECMAScript version our TypeScript code will be compatible with. In case if the code is targeted for all modern browsers, setting `es6` is recommended (so let’s change our `tsconfig.json` to match this), but if the code will be deployed to older environments, it is possible to use `es5` as well. Default value for this setting is `es3`. To use the highest available version, provide `esnext`
  • `module` — sets the module system for the application, recommended option is `commonjs`. Default value for this setting is `commonjs` if `target` is `es3` or `es5`, `es6/es2015` otherwise
  • `esModuleInterop` — emits more JavaScript to ease support for importing CommonJS modules. Default value is `false`
  • `forceConsistentCasingInFileNames` — force TypeScript to issue an error when an application includes a file with a different casing than on the disk. E.g., if the file is named `DateService.ts` and it is tried to be imported as a `dateService.ts`. Default value is `false`
  • `strict` — enables stronger type checking behavior
  • `skipLibCheck` — skip type checking of declaration files. Default value is `false`

Congratulations on getting familiar with the most important configuration options of the TypeScript compiler! Next, let’s write our first TypeScript code sample.

Writing your first lines of Typescript code

The next step is to write a few lines of code with TypeScript and successfully compile them to JavaScript, so let’s create a file named userService.ts in the root folder of the project and populate it with the following code:

interface User {
firstName: string;
lastName: string;
}
const getFullName = (user: User) => `${user.firstName} ${user.lastName}`;

The code defines a function named getFullName that takes a user object as the only argument and returns the full name of the user by combining the first and last names.

After the file is created, we can use the TypeScript compiler to automatically generate JavaScript code based on this example. Assuming you installed globally, execute the tsc command (without any arguments).

Notice that a new file named userService.js has just in the project’s root directory. This is what the generated JavaScript code looks like for the above TypeScript example:

“use strict”;var getFullName = function (user) { return user.firstName + “ “ + user.lastName; };

One important thing to understand before we continue further is how the compiler finds the configuration tsconfig.json file.

The compiler was able to find the configuration file even though we haven’t provided any argument to the tsc command. This happens because the compiler searches for a valid tsconfig.json file in the current directory. If not found, it continues searching for one across its parent directories. However, it is also possible to specify the path to the tsconfig.json file by adding `-p` flag to the `tsc` command in the following format: `tsc -p /path/to/tsconfig.json`

What if we have more than a single file to be compiled? Naturally, we are not limited to having our TypeScript code in a single file. The same compiler configuration file tsconfig.json can be used holistically across the entire project.

Let’s create a second TypeScript code file named projectService.ts with the following content:

interface Project {
id: number;
name: string;
};
const projects: Project[] = [
{
id: 1,
name: “Project #1”,
},
{
id: 2,
name: “Project #2”,
},
];
const getProjectName = (projectId: number) => projects.find((project) => project.id === projectId);

After running tsc this TypeScript code is compilated to JavaScript and stored in a new file named projectService.js.

“use strict”;const projects = [
{
id: 1,
name: “Project #1”,
},
{
id: 2,
name: “Project #2”,
},
];
const getProjectName = (projectId) => projects.find((project) => project.id === projectId);

Note that running tsc without any arguments compiled all `.ts` files in the directory. Therefore, if we make a change in a file that has already been compiled, that will be reflected in the generated `.js` file after a new compilation.

Alternatively, it is possible to compile specific TypeScript files by passing a list of files as tsc parameters such as:

tsc userService.ts or tsc userService.ts projectService.ts

Another possibility in case there are many files in the project would be to define which files should be compiled in the tsconfig.json configuration file, such as:

{
“compilerOptions”: {
// all generated options here
},
“files”: [
“userService.ts”,
“projectService.ts”
]
}

Handling Code Dependencies

When our TypeScript code has imports declared inside of the source code file that are not specified in the tsconfig.json configuration file, the TypeScript compiler will automatically include them in the process. As an example, if the projectService.ts imports something from another source file (e.g., coreService.ts), that will also be included in the compilation process.

Instead of including all files one by one, an industry best practice is to add the files to a separate folder (e.g., src) and instruct the compiler to compile only the files in this directory.

Next, we will create the `src` folder in our project’s root directory and move both the userService.ts and projectService.ts files to it.

We can now change our configuration to compile only the files in this folder by using the `include` configuration option (don’t forget to remove the previously used `files` section before doing this):

{
“compilerOptions”: {
// all generated options here
},
“include”: [
“src/*”
]
}

The inverse scenario, excluding files or folders rather than including them, is also possible by using the `exclude` configuration option as shown in this example:

{
“compilerOptions”: {
// all generated options here
},
“include”: [
“src/*”
],
“exclude”: [
“src/userService.ts”
]
}

As you can see, the configuration file gives a tremendous amount of flexibility to manage what files should (or not) be part of the compilation process. However, it is equally important to define how the output should be handled.

Up until this point, all compiled files were put in the same directory where the original TypeScript file was stored.

As the codebase grows it will be hard to manage this mix of files within the same directory. A good practice is to place the compiled files to a new directory to keep them separated from the main code. This can be achieved by specifying the `outDir` configuration option inside of the `compilerOptions` in the tsconfig.json file:

{
“compilerOptions”: {
// all generated options here
“outDir”: “dist”
}
}

Run the tsc command now and notice how all generated files are now placed inside of the `dist` directory automatically.

If you are using git (or other version control system), don’t forget to add the `dist` folder to the .gitignore file to make sure it’s not pushed to the remote repository.

Next Steps in Your Learning Journey

While there is still much more to learn about TypeScript, its compilation process and possible configuration options, we have now gone through the most important things you need to know to get started with TypeScript.

This article gave you the foundation to understand what TypeScript is, why it’s important and how you can write TypeScript code and compile it to JavaScript. The compiler configuration options we explored enabled you to organize your code according to industry’s best practices and customize it to better fulfill your requirements.

As a next step in your learning journey, we recommend you explore the different TypeScript interfaces and types, and of course, keep experimenting with the code! To learn more about TypeScript please visit the official documentation and guidelines page.

Stay tuned for our next article!

About the Author:

Volodymyr Hudyma, Software Engineer at Fortum

Volodymyr Hudyma

--

--