TypeScript vs JavaScript - the key differences and similarities

TypeScript. It seems that nowadays everyone loves it! Its popularity is skyrocketing and by now, it’s one of the fastest trending languages in recent years. According to a Stack Overflow survey from 2019, it is the 3rd most loved and 4th most wanted programming language by developers. Why has it become so popular? Well, let’s find out why the buzz and how TypeScript looks compared to its older brother, good old JavaScript.

It all started in 1995 with JavaScript, which made websites interactive. Before that time, websites were just simple, static pages. As web applications became more and more popular, modern JavaScript frameworks appeared. Now JavaScript is everywhere, it is used in your browser, on the server side, even your desktop and mobile applications are using JavaScript. Mainly because it is very flexible, dynamically typed and allows the writing of code in many paradigms.

However with this popularity and freedom comes the price of increased complexity, which may be the reason for the many bugs. That’s why TypeScript was created. To help developers make better, scalable projects to the highest standards.

But is TypeScript always better than JavaScript, and what are the exact differences?

In this article, we will try to compare these two languages and find out whether you should use JavaScript or TypeScript in your project.

TypeScript vs JavaScript - the features

First things first. JavaScript is a subset of TypeScript. That means that every JavaScript code is a valid TypeScript code. If you know JavaScript already that means that you will have a relatively flat learning curve and you can easily refactor your project’s code from JavaScript to TypeScript to suit your learning pace.

One of the popular benefits of using TypeScript is that you can use some features which are still in proposal for the next version of JavaScript and which are not yet supported by all browsers. It is possible because TypeScript code is transpiled to the JavaScript version specified in compilerOptions in the configuration file tsconfig.json. JavaScript features from ES6 were available in TypeScript 3 years earlier so developers had an opportunity to get used to the new syntax. It is safe to say that the TypeScript community is speeding up the progress of JavaScript development.

Both JavaScript and TypeScript support object-oriented programming language features like classes and inheritance. Additionally, TypeScript supports abstract classes, interfaces, decorators and access modifiers.

TypeScript code example:

interface Author {
   id: number,
   name: string,
}

interface PostInterface {
   id: number,
   author: Author,
   text: string,
}

class Post {
   private static id = 0;

   author: Author;
   text: string;

   constructor(author: Author, text: string) {
       this.author = author;
       this.text = text;
   }

   public create = (): PostInterface => ({
       id: Post.id++,
       author: this.author,
       text: this.text,
   })
}

Transpiled to ES6 JavaScript:

class Post {
   constructor(author, text) {
       this.create = () => ({
           id: Post.id++,
           author: this.author,
           text: this.text,
       });
       this.author = author;
       this.text = text;
   }
}
Post.id = 0;

TypeScript vs JavaScript: the amount of code to write

As you can see from the example above, when writing TypeScript you have to generally write more code than in JavaScript. Some say that it creates syntax noise. You have to annotate types, create your own specific types, sometimes even model whole business domains using types.

However, using TypeScript, can also save you some time writing code as some things are done automatically for you. In JavaScript, in order to be sure of a variable type, you have to use the typeof operator to check it. Typescript helps you to get rid of that by checking types for you during the transpilation process. You also do not have to annotate types explicitly wherever you can. If they are obvious, TypeScript knows them. This is called type inference.

let account = {
   name: 'John',
   age: 33,
}
//ok
account = {
   name: 'Josh',
   age: 24,
}
//error: Type '{ name: string; age: number; occupation: string; }' is not assignable
//to type '{ name: string; age: number; }'.
account = {
   name: 'Jessica',
   age: 26,
   occupation: 'teacher',
}

TypeScript can also help you shorten the syntax:

class Post {
   private static id = 0;

   constructor(
       private author: Author,
       private text: string,
        ) {}

   create = (): PostInterface => ({
       id: Post.id++,
       author: this.author,
       text: this.text,
   })
}

Adding access modifiers in constructor parameters will automatically initialize those parameters as class members. Also the public access modifier can be omitted because all members of a class are public by default.

TypeScript vs JavaScript: Types

Of course JavaScript also has types. But JavaScript is called a loosely typed language. Variables are not directly associated with any particular type and they can be assigned and reassigned.

TypeScript on the other hand gives you the ability to add static typing to JavaScript. If you assign a type to a variable, you cannot change it later to other types. Also TypeScript comes with a much more advanced type system, including Intersection Types, Union Types, Generics and more. Unlike many C-like languages it has a structural type system, which means that two types are deemed equal if they have the same structure.

Let’s modify our example to illustrate that:

interface PostInterface {
   id: number,
   author: Author,
   text: string,
}
interface Author {
   id: number,
   name: string,
   age?: number, //added optional field
}
//added another interface, similar to Author
interface Admin  {
   id: number,
   name: string,
}
class Post {
   private static id = 0;

   constructor(
       private author: Author,
       private text: string,
        ) {}

   create = (): PostInterface => ({
       id: Post.id++,
       author: this.author,
       text: this.text,
   })
}
let admin = {
   id: 1,
   name: 'admin1',
} as Admin; //casting object to Admin type
let author = {
   id: 2,
   name: 'author1',
   age: 30,
} as Author; //casting object to Author type
const post1 = new Post(author, "author’s post").create();
const post2 = new Post(admin, "admin’s post").create();
console.log(post2);

Even if the Post constructor parameter is declared as an Author type, we are still able to pass another type, Admin, which is the same structurally as the Author type. This is possible because of TypeScript’s structural type system.

If you are using IDE which supports TypeScript, you know the type of a variable and its structure instantly. You do not have to waste time looking for variable declarations or API documentation. As long as you do not assign any type to everything, of course.

Since TypeScript is transpiled into JavaScript, types are checked only in the transpilation process. Interfaces are not part of the JavaScript language so they are omitted in the output JavaScript file, as you can see from the example in this article. One way to handle this is to use type guards.

Let’s look at an example:

interface User {
 accountType: "admin" | "author";
}
interface Author extends User {
 accountType: "author";
 writeArticle(text: string): void;
}
interface Admin extends User {
 accountType: "admin";
}
const isAuthor = (user: Author | Admin): user is Author =>
 user.accountType === "author";

const writeArticle = (user: Author | Admin, text: string) => {
 if (!isAuthor(user)) {
   user.writeArticle(text); // it causes error, because this function does not exist in non Author object
   throw new Error("Only Authors can write articles");
 } else {
   // here the writeArticle func is available and hintable by TS
   return user.writeArticle(text);
 }
};

In the transpiled JavaScript file, we can see that the type guard is in place. So we can be sure that this code will be handled properly.

const isAuthor = (user) => user.accountType === "author";
const writeArticle = (user, text) => {
 if (!isAuthor(user)) {
   // it causes error, because this function does not exist in non Author object
   user.writeArticle(text);
   throw new Error("Only Authors can write articles");
 } else {
   // here the writeArticle func is available and hintable by TS
   return user.writeArticle(text);
 }
};

TypeScript vs JavaScript: Bugs

Bugs happen in JavaScript as well as in TypeScript.

We are all human. But due to the transpilation process we can catch silly bugs, e.g typos, wrong types or syntax errors at this early stage. Using JavaScript, bugs can be missed and in the worst case scenario everything might work fine for some time until the bug is detected. Finding those bugs in the production environment is a lot more time-consuming and costly.

TypeScript vs JavaScript: New data structures

TypeScript introduced new data structures which we know from other languages.

Tuple - an array of a fixed number of elements whose types are known.

let user: [number, string];
user = [1, 'someone']; // ok
console.log(user[0]); // 1
user = ['2', 'someone else']; //Error: Type 'string' is not assignable to type 'number'.

Tuples are used, for example, as Object.entries output types:

const entries: Array<[string, number]>
   = Object.entries({ pieces: 10, price: 1.5 });
// output:  [ [ "pieces", 10 ], [ "price", 1.5 ] ]

Of course, the type is set explicitly for learning purposes. We can skip type annotation due to type inference.

Enum - enumerated type, is a set of named constants. TypeScript provides both numeric and string-based enums.

enum Action  {
   CREATE,
   READ,
   UPDATE = 'update',
   DELETE = 10,
}

function handleAction(action: Action) {
   switch (action) {
       case Action.CREATE:
           console.log('create',action);
           break;
       case Action.READ:
           console.log('read',action);
           break;
       case Action.UPDATE:
           console.log('update',action);
           break;
       case Action.DELETE:
           console.log('delete',action);
           break;
   }
}
handleAction(Action.DELETE);

Enums help you to get rid of magic numbers and string literal values in code which can be mistyped. Also they might help you protect from wrong property values in functions as they limit your possible inputs to the declared ones. See example above which handleAction function requires Action type.

TypeScript vs JavaScript: Domain model

With TypeScript, we can model the whole domain using only TypeScript’s type system. It is time-consuming at the start but it can pay off later. Modeling types can raise some new questions about the business logic of an application and improve the team’s understanding of it. Also, when a new developer joins or another team is taking over the whole project, the initial time spent on understanding the project is reduced.

TypeScript vs JavaScript: Which one is better?

It is hard to compare these two languages since TypeScript is basically JavaScript with additional features. So, purely from the perspective of the number of features the answer is, without a second thought, TypeScript. It retains JavaScript’s flexibility and adds a little structure which helps developers create scalable and maintainable applications. But that does not mean that using JavaScript is not a good option. Used with a good IDE and static code analysis tools, e.g. ESLint, it can be a good option for small applications, prototypes or minimum viable products (MVP).

Conclusions

TypeScript is not a magic wand that will fix your JavaScript application problems. It is rather a powerful tool that can improve the readability and maintainability of complex projects.

For smaller applications, JavaScript is still a good choice but as the project grows, thinking about refactoring the code to TypeScript is a good idea. TypeScript offers the flexibility of JavaScript along with the benefits of a statically typed language. Since it is not that restrictive and has an impact on the most popular language (JavaScript) it is safe to say that TypeScript will not end like CoffeeScript but will be with us for some time from now.

Of course, TypeScript has a lot more features than described in this article and I strongly recommend you look into its vast documentation.