TypeScript Advanced Types — Nullable Types and Type Aliases
Tags: webdev, programming, typescript, javascript
Published: 2021-01-23 20:03:54

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

TypeScript has many advanced type capabilities and which make writing dynamically typed code easy. It also facilitates the adoption of existing JavaScript code since it lets us keep the dynamic capabilities of JavaScript while using the type-checking capability of TypeScript. There are multiple kinds of advanced types in TypeScript, like intersection types, union types, type guards, nullable types, and type aliases, and more. In this article, we look at nullable types and type aliases.

Nullable Types
==============

To let us assign `undefined` to a property with the `--strictNullChecks` flag on, TypeScript supports nullable types. With the flag on, we can’t assign `undefined` to type members that don’t have the nullable operator attached to it. To use it, we just put a question mark after the member name for the type we want to use.

If we have the `strictNullChecks` flag on and we set a value of a property to `null` or `undefined` , then like we do in the following code:

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

let p: Person = {  
  name: 'Jane',  
  age: null  
}
```

Then we get the following errors:

```
Type 'null' is not assignable to type 'number'.(2322)input.ts(3, 3): The expected type comes from property 'age' which is declared here on type 'Person'
```

The errors above won’t appear if we have `strictNullChecks` off and the TypeScript compiler will allow the code to be compiled.

If we have the `strictNullChecks` flag on and we want to be able to set `undefined` to a property as the value, then we can make the property nullable. For example, we can set a member of an interface to be nullable with the following code:

```
interface Person {  
  name: string;  
  age?: number;  
}

let p: Person = {  
  name: 'Jane',  
  age: undefined  
}
```

In the code above, we added a question mark after the `age` member in the `Person` interface to make it nullable. Then when we define the object, we can set `age` to `undefined`. We can’t still set `age` to `null` . If we try to do that, we get:

```
Type 'null' is not assignable to type 'number | undefined'.(2322)input.ts(3, 3): The expected type comes from property 'age' which is declared here on type 'Person'
```

As we can see, a nullable type is just a union type between the type that we declared and the `undefined` type. This also means that we can use type guards with it like any other union type. For example, if we want to only get the age if it’s defined, we can write the following code:

```
const getAge = (age?: number) => {  
  if (age === undefined) {  
    return 0  
  }  
  else {  
    return age.toString();  
  }  
}
```

In the `getAge` function, we first check if the `age` parameter is `undefined` . If it is, then we return 0. Otherwise, we can call the `toString()` method on it, which is available to number objects.

Likewise, we can eliminate `null` values with a similar kind of code, for instance, we can write:

```
const getAge = (age?: number | null) => {  
  if (age === null) {  
    return 0  
  }    
  else if (age === undefined) {  
    return 0  
  }  
  else {  
    return age.toString();  
  }  
}
```

This comes in handy because nullable types exclude `null` from being assigned with `strictNullChecks` on, so if we want `null` to be able to be passed in as a value for the `age` parameter, then we need to add `null` to the union type. We can also combine the first 2 `if` blocks into one:

```
const getAge = (age?: number | null) => {  
  if (age === null || age === undefined) {  
    return 0  
  }  
  else {  
    return age.toString();  
  }  
}
```


Type Aliases
============

If we want to create a new name for an existing type, we can add a type alias to the type. This can be used for many types, including primitives, unions, tuples, and any other type that we can write by hand. To create a type alias, we can use the `type` keyword to do so. For example, if we want to add an alias to a union type, we can write the following code:

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

interface Employee {  
  employeeCode: number;  
}

type Laborer = Person & Employee;  
let laborer: Laborer = {  
  name: 'Joe',  
  age: 20,  
  employeeCode: 100  
}
```

The declaration of `laborer` is the same as using the intersection type directly to type the `laborer` object, as we do below:

```
let laborer: Person & Employee = {  
  name: 'Joe',  
  age: 20,  
  employeeCode: 100  
}
```

We can declare type alias for primitive types like we any other kinds of types. For example, we can make a union type with different primitive types as we do in the following code:

```
type PossiblyNumber = number | string | null | undefined;  
let x: PossiblyNumber = 2;  
let y: PossiblyNumber = '2';  
let a: PossiblyNumber = null;  
let b: PossiblyNumber = undefined;
```

In the code above, the `PossiblyNumber` type can be a number, string, `null` or `undefined` . If we try to assign an invalid to it like a boolean as in the following code:

```
let c: PossiblyNumber = false;
```

We get the following error:

```
Type 'false' is not assignable to type 'PossiblyNumber'.(2322)
```

just like any other invalid value assignment.

We can also include generic type markers in our type aliases. For example, we can write:

```
type Foo<T> = { value: T };
```

Generic type aliases can also be referenced in the properties of a type. For example, we can write:

```
type Tree<T> = {  
  value: T;  
  left: Tree<T>;  
  center: Tree<T>;  
  right: Tree<T>;  
}
```

Then we can use the `Tree` type as we do in the following code:

```
type Tree<T> = {    
  value: T,  
  left: Tree<T>;  
  center: Tree<T>;  
  right: Tree<T>;  
}

let tree: Tree<string> = {} as Tree<string>;  
tree.value = 'Jane';tree.left = {} as Tree<string>  
tree.left.value = 'Joe';  
tree.left.left = {} as Tree<string>;  
tree.left.left.value = 'Amy';  
tree.left.right = {} as Tree<string>  
tree.left.right.value = 'James';tree.center = {} as Tree<string>  
tree.center.value = 'Joe';tree.right = {} as Tree<string>  
tree.right.value = 'Joe';console.log(tree);
```

The `console.log` for `tree` on the last line should get us:

```
{  
  "value": "Jane",  
  "left": {  
    "value": "Joe",  
    "left": {  
      "value": "Amy"  
    },  
    "right": {  
      "value": "James"  
    }  
  },  
  "center": {  
    "value": "Joe"  
  },  
  "right": {  
    "value": "Joe"  
  }  
}
```

Nullable types are useful is we want to be able to assign `undefined` to a property when `strictNullChecks` flag is on when in our TypeScript compiler configuration. It’s simply a union type between whatever type you have and `undefined` . It’s denoted by a question mark after the property name. This means we can use type guards with it like any other union type. Note that nullable types don’t allow `null` values to be assigned to it since nullable types are only needed when `strictNullChecks` flag is on. Type alias let us create a new name for types that we already have. We can also use generics with type alias, but we can’t use them as standalone types.