If you've not encountered it yet, TypeScript is a superset of JavaScript: it builds on top of existing JavaScript code, allowing you to add types (and other goodies!).

A complaint I've heard from a few people is that adding types adds unneeded complexity: it compiles down to the same JavaScript, so why "burden" yourself with navigating a bunch of type errors? If you're hacking on a small project or just trying to create a quick proof–of–concept, I'd be tempted to agree. However, if you're building projects at scale, or simply want a little more assurance from your language, definitely give TypeScript a try.

As a quick example so that you can see the syntax, here's a function that returns a greeting for a given name, firstly in JavaScript:

function greet(name) {
    return `Hello, ${name}.`;
}

And now the same function, but in TypeScript:

function greet(name: string): string {
    return `Hello, ${name}.`;
}

Pretty similar, but there are two differences here: the : string after the name parameter, and the : string after the parameter list. These are type definitions:

  • The string after the name parameter means that the parameter passed in must be a string.
  • The string after the parameter list means that the function returns a string type.

In this example, this is nice, but not really groundbreaking. It offers some assurances if you try and pass in an invalid parameter, for example an array:

Argument of type string[] is not assignable to parameter of type string
TypeScript doesn't even compile if there is a type error contained in our code.

Compiling

To run TypeScript code, you'll first need to compile it to plain old JavaScript*. To do this, you'll need to install TypeScript with npm. So, in an existing node project:

npm i typescript

You can install this globally, and you'll often see articles that suggest you do so, but I prefer to keep all packages locally scoped.

Then, all you need to do is run tsc <filename.ts>. This will spit out a filename.js file, that you can then run with node.

There are lots of additional parameters and flags that you can pass to the tsc command. You can also define these flags in a file called tsconfig.json. You can read more about tsconfig on the TypeScript website.

* You can run TypeScript without compiling, but that's out of the scope of this post. If you're interested, check out ts-node or Deno.

Defining Variables

You can use the type definition syntax above to declare variables and constants.

const name: string = 'Tomy';

For more complex variables, that are represented by an object with properties, you can use an inline definition of the object:

const person: { name: string; age: number; height: number } = {
    name: 'Tomy',
    age: 27,
    height: 182,
};

However, this code can get unruly very quickly, so you should instead create an interface to hold this information.

Interfaces

Interfaces—just like in other languages—allow you to define a group of properties and methods for an object to implement.

The person definition above can be rewritten to:

interface Person {
    name: string;
    age: number;
    height: number;
}

const person: Person = {
    name: 'Tomy',
    age: 27,
    height: 182,
};
Creating a constant called person, that is of the Person type.

What's great is that, if the newly created object doesn't adhere to this definition (for example, you don't include the height property), you'll get an error and TypeScript won't compile the file.

What's even cooler is that, in most editors, you get code completion when creating these new constants. For example, in VS Code, hit Ctrl + Space to bring up a code completion overlay, that shows the properties of the type that you're currently creating.

A code completion overlay showing age, height and name properties.
By hitting Ctrl + Space, we can see that we still need to add an age, height and name property to this constant.

Stay tuned for part two, where I'll cover union types, classes and generics!