6 useful TypeScript 3 features you need to know
Tags: typescript
Published: 2019-12-19 07:23:44

---
title: 6 useful TypeScript 3 features you need to know
published: true
description: A reminder of 6 handy features that have been released in TypeScript version 3
tags: TypeScript
cover_image: https://www.carlrippon.com/wp-content/uploads/2019/12/ts3-1024x403.png
---

TypeScript releases are coming thick and fast, so it's easy to miss some of the handy features that are added. Here are some of the new features that I've found useful in the last year.

## The `unknown` type

When dealing with data from a 3rd party library or web API that hasn't got TypeScript types, we often reach for the `any` type. However, this means no type checking will occur on the data.

```typescript
const person: any = await getPerson(id);
const postcode = person.address.postcode;  // TypeScript doesn't yell at us but what if there is no address property ... 💥!          
```

The `unknown` type is a strongly-typed alternative to `any` in these situations: 

```typescript
const person: unknown = await getPerson(id);
const postcode = person.address.postcode; // 💥 - Type error - Object is of type 'unknown'
```

The `unknown` type forces us to do explicit type checks when interacting with it:

```typescript
const person: unknown = await getPerson(id);
if (isPersonWithAddress(person)) {
  const postcode = person.address.postcode; 
}

function isPersonWithAddress(person: any): person is Person {
  return "address" in person && "postcode" in person.address;
}
```


## Readonly arrays

We may think the following would raise a type error:

```typescript
type Person = {
  readonly name: string;
  readonly scores: number[];
}
const bob: Person = {
  name: "Bob",
  scores: [50, 45]
}

bob.scores.push(60);  // does this raise a type error?
```

TypeScript is perfectly happy with this, though. The `readonly` keyword before an array property name only ensures the property can't be set to a different array - the array itself isn't immutable.

However, we can now put an additional `readonly` keyword before the array itself to make it immutable:

```typescript
type Person = {
  readonly name: string;
  readonly scores: readonly number[];
}
const bob: Person = {
  name: "Bob",
  scores: [50, 45]
}
bob.scores.push(60);  // 💥 - Type error - Property 'push' does not exist on type 'readonly number[]'
```

There is also a `ReadonlyArray` generic type that does the same thing:

```typescript
type Person = {
  readonly name: string;
  readonly scores: ReadonlyArray<number>;   // same as readonly number[]
}
```

## `const` assertions

`const` assertions help us create immutable structures:

```typescript
function createGetPersonAction() {
  return { type: 'GetPerson' } as const;
}
const getPersonAction = createGetPersonAction();  // `getPersonAction` is of type `{ readonly type: "GetPerson"; }`
```

Here's another example, this time using the alternative angle bracket syntax for the `const` assertion:

```typescript
type Person = {
  id: number;
  name: string;
  scores: number[];
}
const people: Person[] = [
  { id: 1, name: "Bob", scores: [50, 45] },
  { id: 2, name: "Jane", scores: [70, 60] },
  { id: 3, name: "Paul", scores: [40, 75] }
]
function getPersonScores(id: number) {
  const person = people.filter(person => person.id === id)[0];
  return <const>[...person.scores];
}

const scores = getPersonScores(1);  // `scores` is of type `readonly number[]`
scores.push(50);                    // 💥 - Type error - Property 'push' does not exist on type 'readonly number[]'
```

`const` assertions also prevent literal types from being widened. The example below raises a type error because `Status.Loading` is widened to a string:

```typescript
function logStatus(status: "LOADING" | "LOADED") {
  console.log(status);
}

const Status = {
  Loading: "LOADING",
  Loaded: "LOADED",
};

logStatus(Status.Loading); // 💥 - Type error - Argument of type 'string' is not assignable to parameter of type '"LOADING" | "LOADED"'
```

Using a `const` assertion will result in `Status.Loading` maintaining its type of `"LOADING"`:

```typescript
const Status = {
  Loading: "LOADING",
  Loaded: "LOADED",
} as const ;

logStatus(Status.Loading); // Type is "LOADING"
```

Marius Schulz has an [in depth post](https://mariusschulz.com/blog/const-assertions-in-literal-expressions-in-typescript) on `const` assertions. 


## Optional chaining

Optional chaining allows us to deal with object graphs where properties may be `null` or `undefined` in an elegant manner.

Consider the following example:

```typescript
type Person = {
  id: number;
  name: string;
  address?: Address;
}
type Address = {
  line1: string;
  line2: string;
  line3: string;
  zipcode: string;
}
function getPersonPostcode(id: number) {
  const person: Person = getPerson(id);
  return person.address.zipcode; // 💥 - Type error - Object is possibly 'undefined'
}
```

The code raises a type error because the `address` property is optional and, therefore, can be `undefined`. The optional chaining operator (`?`) allows us to navigate to the `zipcode` property without generating any type errors or runtime errors:

```typescript
function getPersonPostcode(id: number) {
  const person: Person = getPerson(id);
  return person.address?.zipcode; // returns `undefined` if `address` is `undefined`
}
```

If the `address` property is `undefined` at runtime, then `undefined` will be returned by the function.

## Nullish coalescing

Nullish coalescing allows us to substitute a different value for a value that is `null` or `undefined`.

Consider the following example:

```typescript
type Person = {
  id: number;
  name: string;
  score?: number;
}

function getPersonScore(id: number): number {
  const person: Person = getPerson(id);
  return person.score; // 💥 - Type error - Type 'number | undefined' is not assignable to type 'number'
}
```

The code raises a type error because the `score` property is optional and, therefore, can be `undefined`. The nullish coalescing operator (`??`) allows a different value (specified in the right-hand operand) to be used when the left-hand operand is `null` or `undefined`:

```typescript
function getPersonScore(id: number): number {
  const person: Person = getPerson(id);
  return person.score ?? 0; 
}
```

The nullish coalescing operator is more robust than using `person.score || 0` because `0` will be returned for any [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) value rather than just `null` or `undefined`. 

## The `Omit` utility type

The `Omit` utility type allows a new type to be created from an existing type with some properties removed.

Consider the following example:

```typescript
type Person = {
  id: number;
  name: string;
  mobile: string;
  email: string;
}
```

If we want to create a `Person` type without any of the contact details we can use the `Omit` utility type as follows:

```typescript
type PersonWithoutContactDetails = Omit<Person, "mobile" | "email">  // { id: number; name: string; }
```

The benefit of this is that the `PersonWithoutContactDetails` type will change if `Person` changes without us having to touch `PersonWithoutContactDetails`:

```typescript
type Person = {
  id: number;
  name: string;
  mobile: string;
  email: string;
  age: number;
}

// PersonWithoutContactDetails automatically becomes { id: number; name: string; age: number;}
```

*Originally published at [https://www.carlrippon.com/6-useful-typescript-3-features-you-need-to-know/](https://www.carlrippon.com/6-useful-typescript-3-features-you-need-to-know/) on Dec 18, 2019*.