TypeScript: Advanced Types

vkton115

Vincent Tong

Posted on August 15, 2022

TypeScript: Advanced Types

In this blog we're going to deep dive into some of the more advanced built-in types included in TypeScript. If you are new to TypeScript I recommend starting with some of my beginner tutorial blogs:

Type Aliases

In our last lesson we created this TypeScript object:

let contactInfo: {
  name: string,
  phone: number,
  fax?: number
} = {name: 'Vincent', phone: 123_456_7890}
Enter fullscreen mode Exit fullscreen mode

This is great but it poses a few problems for us:

1.If we want to create a new contact info object, we would have to repeat this structure:

problem 1

2. The other contact info object may have other properties so the shape may vary.

3. The overall structure may be difficult to interpret at a glance.

This is where Type Aliases come in handy to create custom types. To do this, initiate the keyword 'type' followed by the name of your object in Pascal case (first letter in each word uppercased) like so:

type ContactInfo = {
  name: string,
  phone: number,
  fax?: number,
}
Enter fullscreen mode Exit fullscreen mode

Now that we have the structure defined in our alias, we can remove it from our previous object and create a new object with our new custom typing:

let myContactInfo: ContactInfo = {
  name: "Vincent",
  phone: 123_456_7890,
}
Enter fullscreen mode Exit fullscreen mode

Unions

Unions are a fairly simple concept. You can use them to give additional typings to variables or function parameters. This is done with the "|" character:

function addTwo(num: number | string): number{
  return num + 2;
}
Enter fullscreen mode Exit fullscreen mode

Breaking it down, what this function is saying is that it takes one argument that can either be a number, or a string. While the above function can accept either, there is a possibilty that it may not return a number as required (when you add a string to a number, the result is a string).

note: In the previous blog we talked about Code Completion and how it is one of the benefits TypeScript provides us. However, when we use Unions like in this example, the only methods we will be able to see are the methods that are shared by both strings and numbers:

code completion

So within our function we can add some conditional logic to clear the air:

function addTwo(num: number | string): number{
  if (typeof num === "number") {
    return num + 2;
  } else {
    return parseInt(num) + 2;
 }
}
Enter fullscreen mode Exit fullscreen mode

This process of using conditional logic to find out the typing of the argument is what is known as Type Narrowing.

And now with inference TypeScript will know that num in the first condition will be a number type and num in the 'else' condition must be a string and therefore, the respective type methods will be made available once again with code completion.

Intersections

The concept of intersections types are similar to unions. However, instead of allowing the value to be passed in to be one type OR the other, it allows the variable to be both types at the same time. Our previous example would not be the best way to show this as an object cannot be both a number AND a string at the same time, but lets try it with some custom typings.

Imagine if we are creating simple video game entity.
For some entities we want them to be able to move only left or right like a goomba in the Mario games!

goomba
note this is not necessarily how the actual video game entities were coded but just a visual analogy.

For others we may want them to be able to move up and down like the piranha plant.

piranha plant
our custom types might look something like this:

type LeftAndRight = {
  moveLeft: () => <...>,
  moveRight: () => <...>
}

type UpAndDown = {
 moveUp: () => <...>,
 moveDown:() => <...>
}
Enter fullscreen mode Exit fullscreen mode

But what if we wanted an entity that can go both left & right AND up & down like the flying koopa troopa.

flying koopa

To make a custom type that has the attributes of already existing/custom types that we created, we can use the '&' symbol like so:

type UpDownLeftAndRight = LeftAndRight & UpAndDown;
// and now we can create a variable of that combined type
let flyingKoopaTroopa: UpDownLeftAndRight = {
  moveLeft: () => <...>,
  moveRight: () => <...>,
  moveUp: () => <...>,
  moveDown: () => <...>,
}
Enter fullscreen mode Exit fullscreen mode

Literal Types

We learned that with TypeScript we can assign variables to specific data types like strings. But we can also specify specific strings by assigning the 'type' to the specific string like so:

type CoinFlip = 'heads' | 'tails';
//here we are creating a custom type that can only be of two values

let firstFlip: CoinFlip = 'heads'; //this is ok
let secondFlip: CoinFlip = 'tails'; //this is also ok
let thirdFlip: CoinFlip = 'a crow took it'; //this is not ok
Enter fullscreen mode Exit fullscreen mode

Nullable Types

By default TypeScripts configuration does not allow for null types when assigning variables to specific types. However, if you wish to allow for a variable to be null, you can specify it with the union operator:

let greeting: string | null
//we are saying that greeting can be either a string or null
greeting = null // this is ok
greeting = 'Hello!' // this is also ok
greeting = undefined // error

// if you wish to also specify that greeting can also be undefined, you would need to add another union to include 'undefined' types
Enter fullscreen mode Exit fullscreen mode

Congratulations, you are now an expert on TypeScript types! I hope this post has been informative and will save you plenty of debugging time in the future.

💖 💪 🙅 🚩
vkton115
Vincent Tong

Posted on August 15, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related