TypeScript Types Deep Dive - Part 1
Tags: typescript, javascript, webdev
Published: 2019-12-15 18:47:53

---
title: TypeScript Types Deep Dive - Part 1
published: true
description: An epic journey of discovery into the mysterious world of TypeScript's type system.
tags: [typescript,javascript,webdev]
canonical_url: https://www.barbarianmeetscoding.com/blog/2019/12/07/typescript-types-deep-dive-part-i
cover_image: https://www.barbarianmeetscoding.com/images/typescript-types-deep-dive.jpg
series: TypeScript Types Deep Dive
---

*This article was originally published on [Barbarian Meets Coding](https://www.barbarianmeetscoding.com/blog/2019/12/07/typescript-types-deep-dive-part-i).*

TypeScript is a modern and safer version of JavaScript that has taken the web development world by storm. It is a superset of JavaScript that adds in some additional features, syntactic sugar and static type analysis aimed at making you more productive and able to scale your JavaScript projects.

> 
> ### Haven't read so much about TypeScript yet?
>
> This series of articles assumes that you have some knowledge of TypeScript. If you haven't dabbled so much into the world of TypeScript just yet, I recommend you to take a look at [this introductory article](https://www.barbarianmeetscoding.com/blog/2017/08/17/typescript-javascript-and-types-equal-awesome-developer-productivity).
>

TypeScript was first launched in 2012, and at the time it did bring a lot of new features to JavaScript. Features that wouldn't be available in JavaScript until much later with ES2015 and beyond. Today however, the gap in features between TypeScript and JavaScript is closing, and what remains as TypeScript strongest value proposition is its amazing type system and the dev tools around it. This type system is the one that delivers on the promise of TypeScript: **JavaScript that scales** and what brings you a great develop experience with:

* Instant feedback whenever you do something dumb
* Powerful statement completion
* Seamless semantic code navigation 
* Smart refactorings and automatic code fixes
* And [more](https://www.barbarianmeetscoding.com/blog/2017/08/17/typescript-javascript-and-types-equal-awesome-developer-productivity#so-what-are-the-advantages-and-disadvantages-of-typescript)

In this series of articles we'll explore TypeScript's comprehensive type system and learn how you can take advantage of it to build very robust and maintainable web apps.

## Type Annotations

Type annotations are the core of TypeScript's type system. They are extra tidbits of information that you provide when you write your code so that TypeScript can get a better understanding of it and provide you with a better developer experience.

Let's say you have a function to add two numbers:

```typescript
const add = (a, b) => a + b;
```

Only TypeScript has no idea that neither `a` nor `b` are supposed to be numbers. So we can be slightly more expressive and annotate these params with a type annotation:

```typescript
const add = (a: number, b: number) => a + b;
```

Now TypeScript knows for a fact, that both `a` and `b` can only be numbers. So that if we, for some reason, decide to write the following bit of code:

```typescript
add(1, 'banana');
```

The TypeScript compiler, our faithful companion, will look at our code and go bananas (it expected numbers and we gave it a fruit, how naughty).

{% youtube cboI_Yf3g6Q %}

What's the best part about that? The best part is that we get this error immediately. Not within hours, or days or weeks when this code gets exercised in some production system by an unwary user. Nope! We'll get this error within milliseconds of having introduced it. Great stuff. Short feedback loops. They make everything better. Like bacon, or... bacon.

## Basic Types

> ### Would you like to Experiment with TypeScript type system quickly?
> 
> A great way to experiment with TypeScript and its type system without even having to install TypeScript is to jump right into coding using the [typescriptlang.org TypeScript playground](https://www.typescriptlang.org/play/index.html). Jump in, type some code, and hover over the variables, functions and classes to find their types.
>

The basic types in TypeScript correspond to the primitive types of JavaScript:

```typescript
number
boolean
string
Date
Array<T>
Object
```

So that, if you want to define a string in TypeScript you'd type the following:

```typescript
let myName: string = "Jaime";
```

Because TypeScript's goal is to make your life easy, in situations like this it'll be smart enough to infer the type of the `myName` variable so that you don't need to explicitly annotate it. Which means that this is enough:

```typescript
let myName = "Jaime";    // Type string
```

And so...

```typescript
let myName = "Jaime";    // Type string
let myAge = 23;          // Yeah sure! Type number
```

And:


```typescript
let myName = "Jaime";    // Type string
let myAge = 23;          // Yeah sure! Type number
let isHandsome = true;   // Type boolean
let birth = new Date();  // Type Date
```

> TypeScript will always try to do its best to make sense of your code. The TypeScript compiler has this mechanism called **type inference** by which it will try to interpret your code and find out the types of things so that you yourself don't need to type everything by hand.


## Let vs Const

So if:

```typescript
let myName = "Jaime";    // Type string
```

What is the type of the `myName` variable below?

```typescript
const myName = "Jaime";    // Type ?
```

is it `string`? Is it `const string`? `STRING`? Is it a something else?

If you are like me, and you've never considered this conumdrum you may be as suprised (as I was) to find out that the type is `"Jaime"` (*waaaaat?!?*):

```typescript
const myName = "Jaime";    // Type "Jaime"
```

If we expand the example to other primitive types we'll see that:

```typescript
const myName = "Jaime";    // Type "Jaime"
const myAge = 23;          // Type 23
const isHandsome = true;   // Type true
const birth = new Date();  // Type Date
```

What's going on here? `const` in JavaScript and TypeScript means that these variables above can only be bound once as they are declared. Therefore, TypeScript can make the assumption that these variables will never change and constraint their types as much as it can. In the example above, that means that the type of the constant `myName` will be the **literal type** `"Jaime"`, the type of `myAge` will be `23` and so forth.

**And what about the Date?** Why doesn't **const** affect its type at all? The reason for that is that, since Dates can be changed any time, TypeScript cannot constraint their type further. That date may be **now**, right now, but someone could go and change it to **yesterday** any time **tomorrow**. Oh my.

Let's take a closer look at literal types, what they are and why they are useful.

## Literals Types

So:

```typescript
const myName = "Jaime";    // Type "Jaime"
```

The type of the string above is `"Jaime"` itself. What does that mean? It means that the only valid value for the `myName` variable is the string `"Jaime"` and no other. These are what we call **literal types** and you can use them as any other type annotations in TypeScript:

```typescript
const myName : "Jaime" = "Jaime";
```

So that if I try to be super smart and write the following:

```typescript
const myName : "Jaime" = "John";
```

TypeScript will righteously step in with a compiler error:

```typescript
const myName : "Jaime" = "John";
// => 💥 Type '"John" is not assignable to type '"Jaime"'
```

Awesome! So **How is this useful**? We'll see in just a sec. But in order to give you a really nice example I first need to teach you another cool feature in TypeScript's type arsenal: **unions**.

## Unions

Imagine we are building a library that lets you create beautiful visualizations using SVG. In order to set the properties on an SVG element it'd be helpful to have a function that could look something like this:

```typescript
function attr(element, attribute, value) {}
```

The type of each one of these attributes could be expressed as follows:

```typescript
function attr(element: SVGCircleElement, 
              attribute: string, 
              value: string) {}
```

And you could use this function like so:

```typescript
attr(myCircle, "cx", 10);
attr(myCircle, "cy", 10);
attr(myCircle, "r", 5);
```

This works but... What if you misspell an attribute?

```typescript
attr(myCircle, "cx", 10);
attr(myCircle, "cy", 10);
attr(myCircle, "radius", 5); 
// => 💥 Doesn't work! There's no radius in SVGCircleElement
```

It blows up sometime at runtime. And although it may not explode outright, it won't work as you expected it to. But isn't this exactly what a type system and TypeScript should help you with? Exactly! A better approach is to take advantage of TypeScript type system and use type literals to further constraint the number of possible attributes:

```typescript
function attr(element: SVGCircleElement,
              attribute: "cx" | "cy" | "r",
              value: string) {}
```

**The `"cx" | "cy" | "r"` is a **union type** and represents a value that can either be of type `"cx"`, `"cy"` or `"r"`**. You build union types using the `|` union type operator.

Excellent! So if we now make the same mistake than we just made a second ago, TypeScript will come to the rescue and give us some feedback instantaneously:

```typescript
attr(myCircle, "cx", 10);
attr(myCircle, "cy", 10);
attr(myCircle, "radius", 5); 
// => 💥 Type '"radius"' not assignable to type "cx" | "cy" | "r"
// 🤔 Oh wait! So the radius attribute in a circle is actually called "r"!
```

By taking advantage of type literals you can constraint the available types to only the ones that make sense and create a more robust and maintainable application. As soon as we make a mistake like the one above, TypeScript will tell us and we'll be able to fix it right then and there. Not only that, by making this rich type information available to TypeScript, the TypeScript compiler will be able to offer us more advanced features like statement completion and give us suggestions for suitable attributes as we type in our editor.

If you've done SVG visualizations in the past, the function above may look familiar. That's because it is heavily inspired by `d3.Selection.attr` function:

```typescript
d3.select("svg")
  .attr("width", 100)
  .attr("height", 200)
```

In a past project we run into several of these issues and we ended up creating boilerplate around d3 to avoid misspellings. After migrating to TypeScript we never had the same issue. We could rely on the expressiveness of type system to take care of that on its own.

```typescript
// A possible (naive) type definition for d3Selection
interface d3Selection {
  attr(attribute: 'width' | 'height' | etc..., value: number);
}
```

> ### Really? Would I need to add all attribute names by hand?
>
> The example above was meant to help you understand the usefulness of literal types and how to create type unions. However, it may have given you the false impression that you need to type in all the properties for an SVGElement by hand. Although you can do that, TypeScript has far more interesting features that can make working with types and extracting type information from existing types really convenient. In later articles in this series you'll learn about an alternative way to type the function about using generics and the keyof operator.

## Type Aliases

An attribute type defined as we did earlier can be confusing and cumbersome to reuse:

```typescript
function attr(element: SVGCircleElement,
              attribute: "cx" | "cy" | "r",
              value: string) {}
```

**Type aliases** are a convenient shorthand to describe a type, something like a nickname that can be used to provide a more descriptive name for a type and allow you to reuse it around your codebase. 

So if we wanted to create a type that could represent all the available attributes in an `SVGElement` a way to go about that would be to create an alias like so:

```typescript
type Attribute = "cx" | "cy" | "r" // etc...
```

Once defined we can rewrite `attr` function signature:

```typescript
function attr(element: SVGCircleElement,
              attribute: Attribute,
              value: string) {}
```

## Arrays, Tuples and Objects

You can type **an array** in TypeScript by using the following notation:

```typescript
let numbers: number[] = [1, 2, 3];
```

Or alternatively:

```typescript
let numbers: Array<number> = [1, 2, 3];
```

I like the former because it involves less typing. Since we're just initializing a variable TypeScript can infer the type, so in this case you can remove the type annotation:

```typescript
// TypeScript can infer that the type 
// of numbers is number[]
let numbers = [1, 2, 3];

numbers.push('wat');
// 💥 Argument of type '"wat"' is not assignable to parameter of type 'number'.
numbers.push(4);
// ✅ Yes!
numbers.psuh(5);
// 💥 Property 'psuh' does not exist on type 'number[]'.(2339)
```

TypeScript also has great support for **tuples** which can be seen as finite arrays of two, three (*triplet*), four (*quadruplet*), or more elements. They come in handy when you need to model a number of finite items that have some relationship between them.

We can define a tuple of two elements like this:

```typescript
let position: [number, number] = [0, 0];
```

If we now try to access an element outside of the boundaries of the tuplet TypeScript will come and save us:

```typescript
let something = position[2];
// 💥 Tuple type '[number, number]' of length '2' has no element at index '2'.
```

We can follow a similar approach to define tuples with more elements:

```typescript
let triplet: [number, number, number];
let quadruplet: [number, number, number, number];
let quintuplet: [number, number, number, number, number];
// etc...
```

On occasion you'll find yourself using objects in TypeScript. This is how you type an object literal:

```typescript
const position: {x:number, y:number} = {x: 0, y: 0};
```

Again, under these circumstances TypeScript can infer the type of the object literal so the type annotation can be omitted:

```typescript
const position = {x: 0, y: 0};
```

If you are daring enough to try an access a property that isn't defined in the object's type, TypeScript will get angry at you:

```typescript
const position = {x: 0, y: 0};

console.log(position.cucumber);
// 💥 Property cucumber doesn't exist in type {x:number, y:number}
```

Which is to say that TypeScript gives you **MAXIMUM MISPELLING[^1] PROTECTION**.

[^1]: I misspelled misspelling. ha. ha.

And just like we used type aliases earlier to have a more descriptive and less wordy way to refer to an HTML attribute, we can follow the same approach for object types:

```typescript
type Position2D = {x: number, y: number};
const position: Position2D = {x: 0, y: 0};
```

Which also results in a somewhat more specific error message:

```typescript
console.log(position.cucumber);
// 💥 Property cucumber doesn't exist in type Position2D
```

## Intersections

Where the `|` union operator behaves like an **OR** for types, the `&` intersection operator behaves like an **AND**.

Say you have a type that defines a dog, which is something that has the ability to `bark`:

```typescript
type Dog = {bark():void};
```

And another type that describes something which can be drawn:

```typescript
type CanBeDrawn = {brush:Brush, paint():void}; 
```

We can merge both concepts into a new type that describes a dog which can be drawn using the `&` operator:

```typescript
type DrawableDog = Dog & CanBeDrawn;
```

**How are intersection types useful**? They allow us to model **mixins** and **traits** with types in TypeScript, both patterns that are common in JavaScript applications. A **mixin** is a reusable bit of behavior that can be applied ad hoc to existing objects and classes, and extends them with new functionality. The `&` operator lets you create new types that are the result of combining two or more other types, just like mixins in JavaScript. If you aren't super familiar with mixins I wrote a bunch about their strengths and weaknesses:

* [Object composition with mixins](https://www.barbarianmeetscoding.com/blog/2015/12/28/black-tower-summoning-object-composition-with-mixins)
* [Safer JavaScript object composition with traits](https://www.barbarianmeetscoding.com/blog/2016/01/04/safer-javascript-object-composition-with-traits-and-traits-dot-js)
* [Ultra flexible JavaScript OOP with stamps](https://www.barbarianmeetscoding.com/blog/2016/01/18/javascript-ultra-flexible-object-oriented-programming-with-stamps)

>  The union | and intersection & operators are what we call type operators in TypeScript. Just like operators such as + or - allow you to perform operations on values, the type operators perform operations within the mystical universe of types.

## Wrapping Up

TypeScript's expressive type system is, without the shadow of a doubt, the most interesting feature in the language and what makes it deliver on its promise of writing JavaScript that scales. 

Using type annotations, you can provide additional type information to the TypeScript compiler so that in turn it can make your life as a developer easier, helping you build more robust and maintainable applications. Following that same philosophy, the TypeScript compiler will do its best to infer the types from your code without you having to explicitly annotate every single part of it.

The type annotations at your disposal are many and varied, from primitive types like `number`, `string`, to arrays, arbitrary objects, tuples, interfaces, classes, literal types and more. You can even define type aliases to provide descriptive names that make types easier to understand and reuse.

A particularly interesting set of types are type literals. Type literals represent a single value as a type. They are very useful because they allow you to constraint very finely the type of a variable or API. We saw an example of how you can take advantage of literal types to provide a safer API for the d3 visualization library.

Using type operators like union `|` or intersection `&` you can transform types into other types. This expressiveness and malleability of the type system allows you to model highly dynamic object oriented design patterns like mixins.

And that was all for today! Hope you have enjoyed this article which will be soon be followed by more TypeScript type goodness. Have a wonderful day!