TypeScript: Advanced Types
Tags: typescript, tutorial, javascript, webdev
Published: 2022-08-15 00:28:00

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:

- [TypeScript: Easy Start Guide](https://dev.to/vkton115/easy-start-guide-typescript-3ip4)

- [TypeScript: Basic Types](https://dev.to/vkton115/typescript-standard-built-in-types-485c).


## Type Aliases
In our last lesson we created this TypeScript object:

```js
let contactInfo: {
  name: string,
  phone: number,
  fax?: number
} = {name: 'Vincent', phone: 123_456_7890}
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhghdw7bjtshwrh2ucmk.JPG)

**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:

```js
type ContactInfo = {
  name: string,
  phone: number,
  fax?: number,
}
```
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:
```js
let myContactInfo: ContactInfo = {
  name: "Vincent",
  phone: 123_456_7890,
}
```
## 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:

```js
function addTwo(num: number | string): number{
  return num + 2;
}
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q9is2d7xmeapzg183e5.JPG)

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

```js
function addTwo(num: number | string): number{
  if (typeof num === "number") {
    return num + 2;
  } else {
    return parseInt(num) + 2;
 }
}
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yk2e34d81mj67mw0sde2.png)
*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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l57z31owdwi1zud2b1z1.png)
our custom types might look something like this:

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

type UpAndDown = {
 moveUp: () => <...>,
 moveDown:() => <...>
}
```
But what if we wanted an entity that can go both left & right AND up & down like the flying koopa troopa.

![flying koopa](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gmsph7i8uab94uztax00.png)

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

```js
type UpDownLeftAndRight = LeftAndRight & UpAndDown;
// and now we can create a variable of that combined type
let flyingKoopaTroopa: UpDownLeftAndRight = {
  moveLeft: () => <...>,
  moveRight: () => <...>,
  moveUp: () => <...>,
  moveDown: () => <...>,
}
```
## 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:

```js
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
```
## 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:

```js
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
```

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.