typescript logo

TypeScript is a custom version of JavaScript built by Microsoft that adds support for static typing, classes, enums, and interfaces. Using it for a large JavaScript project can make your code more organized and easier to integrate.

What Is TypeScript?

TypeScript isn’t a new language. It is a syntactical superset of JavaScript—any valid JS code is valid TypeScript code. TypeScript compiles down to plain JavaScript that runs in any browser. Because it’s a purely additive language, the translation layer is very simple, and it’s easy to adopt in an existing JavaScript project.

The main thing TypeScript adds is, of course, static typing. JavaScript is inherently dynamically typed. This means that any variable or object can have its type reassigned at runtime. This makes JS very flexible and easy to use, but unfortunately makes it messy when working in a large project.

With static typing, you’re forced to define what parameters functions and objects take, so if you have a function that takes a string argument, you can’t pass it a number, and if you try to, the compiler will throw an error.

For example, this basic function takes a message parameter that has been assigned the type of string with the x: type syntax. This is a JavaScript primitive, but you can also define your own custom types and structs. The function itself also returns a string value. When we use this function later in the code, we can hover over it and view what types the function takes, and what it returns.

typescript hello world

While this may seem like unnecessary extra work, it leads to very organized codebases, and can improve productivity overall. One of the primary benefits that static typing brings is richer IDE support—because types are statically defined, the IDE can check for simple errors much more easily.

Here’s an example. We’ll use VS Code for the IDE, because it comes with built-in TypeScript support from Microsoft, but TypeScript plugins are available for many text editors. In this app, we have a simple React component that takes two properties and renders some text. In vanilla JS, that would look like this:

vanilla JS

With TypeScript, we can’t pass the props variable without assigning it a type, hence the error above. You can opt out of this checking by using props: any, which makes it explicitly dynamically typed, but it’s better to just define the type above:

define a type

This interface requires props to be an object with two keys, both set to strings. Optionally, you can define this type inline, but it’s better to name things like this for reference elsewhere in the project.

If we go to use this component elsewhere in the application, the compiler will throw an error, saying that the Hello component is missing the two properties defined in HelloProps. If this was a larger more complicated component, you could have missed defining a prop correctly, run into issues when testing, and eventually realize your mistake after a minute or two of debugging. With TypeScript, VS Code will bug you about it beforehand.

error without passing props

Better yet, you’ll get autocomplete features as you type. VS Code will tell you what arguments need as you’re typing them out.

autocomplete

And, if you’re working in a large project (or with TypeScript-based dependencies), you’ll be able to get inline documentation by peeking the type definitions:

peek definition

These kinds of features simply don’t exist with vanilla JavaScript without manually writing extensive inline documentation.

Overall, TypeScript is very useful for teams of coders looking to organize their JavaScript codebases. The switch from JavaScript to TypeScript isn’t too hard; it’s easy to pick up if you already know JavaScript, and will likely be preferable to people who have worked with other statically typed languages like Java and the C variants.

If you’d like to get started with TypeScript, you can read our guide on setting it up (and working with it in an existing JavaScript codebase).

Other TypeScript Features

TypeScript doesn’t stop with just adding static types—it adds a whole bunch of syntactical sugar over vanilla JS. We recommend giving their Handbook a read, but we’ll discuss some of the coolest ones here as well.

Enums, or Enumerators, are a way of defining a set of named constants. For example, Up, Down, Left, or Right. You can use these to define a custom type that allows for a specific list of values. You can reference the enum literal using EnumName.constant:

define custom type that allows specific value list using enum

Interfaces allow you to define custom structures that you can use as types. Beyond assigning basic primitive types, you’ll probably be using these the most. For example, you could have a User interface that defines an object with a username, email address, and ID, then create a function that takes a list of Users and looks up their friends from a database.

You can also use these as more traditional interfaces, implementing them in a class and requiring that the fields and methods for that class must be implemented in code. For example, the User interface can require a getPosts() function to be implemented, which returns an array of Post objects. This can help you standardize class-to-class communication.

Interfaces define custom structures to use as types

Generics allow you to pass type information through functions that can accept any type. Without generics, the best you can do is a function like the following, which takes any type of argument, and returns a variable that has the any type.

function identity(arg: any): any { return arg;
}

This retains the value passed to it, but loses all type information associated with it. Instead, you can set a type variable, usually T, which will carry the type data. This new function will return whatever type you pass into it:

function identity<T>(arg: T): T { return arg;
}

You can use this identity function in your code, and pass it both strings and numbers, and TypeScript won’t complain. It automatically infers the type based on the argument passed to it, but if you want to set a type manually, you can do so by passing the type name in brackets:

identity<string>()