5 TypeScript features that will improve your codebase
Tags: typescript, webdev, tutorial
Published: 2020-06-22 18:25:37

The emergence of typescript changed the way how developers deal with javascript applications. It brings not only code reliability with type-safety,  but also improves developer experience, by providing powerful refactoring capabilities and smarter ways to write JS code.

In this post I would like to talk about five typescript features, that will improve your codebase.

### Table of content

- [Nullish coalescing](#Nullish_coalescing)
- [Type guards](#Type_guards)
- [Enum object keys](#Enum_object_keys)
- [Generic interfaces](#Generic_interfaces)
- [Generic function parameter types](#Generic_function)

### <a name="Nullish_coalescing"></a>1. Nullish coalescing

Let's consider the following example.

```jsx
function getNumberOrDefault(value?: number) {
  return value || 22;
}
```

In the mentioned example, we would like to return a default value, if the provided value is undefined. However, this code has a severe pitfall — javascript treats `0` as a `falsy` value.

```jsx
console.log(getNumberOrDefault(10)) // Output 10
console.log(getNumberOrDefault(0)) // Output 22
```

Returning `22` is probably, not something we expect this function to do. Thanks to the nullish coalescing we don't need to refactor the code and add check for `0`, instead we can use a shorthand syntax with `??`.

```jsx
function getNumberOrDefaultTs(value?: number) {
	return value ?? 22;
}

console.log(getNumberOrDefault(10)) // Output 10
console.log(getNumberOrDefault(0)) // Output 0
```

### <a name="Type_guards"></a>2. Type guards

Let's imagine we have two types, `Truck` and `F1`, which extend the same interface `Car`. Although they share some common properties, they also possess a set of unique attributes like `load` and `acceleration` respectively.

```jsx
interface Car {
  model: string;
}

interface Truck extends Car {
  load: number;
}

interface F1 extends Car {
  acceleration: number;
}

function scanCar(car: Truck | F1) {
	...some code
}
```

Also, we have a function `scanCar` which accepts either a `Truck` or a `F1` and performs some type-specific actions.

Type guard is a great way to let typescript know with which type we are currently dealing.

```jsx
function carIsTruck(car: Truck | F1): car is Truck {
  return 'load' in car;
}

function scanCar(car: Truck | F1) {
  if(carIsTruck(car)) {
		// Only truck properties will be suggested
    console.log(`Truck has a load of ${car.load}`)
  } else {
		// Only F1 properties will be suggested
    console.log(`F1 has acceleration of ${car.acceleration}`)
  }
}
```

Type guard is a function that returns a boolean, which is often used as part of the conditional statement, to let typescript know which type is assumed in the current if block.

### <a name="Enum_object_keys"></a>3. Enum object keys

There are some cases when we want to enforce object keys being certain values only. And here is how we can achieve it using `enum` and `Record`.

```jsx
enum Size {
  M,
  L,
  XL
}

function getPlainTextSizes(): Record<Size, string> {
  return {
    [Size.M]: "medium",
    [Size.L]: "large",
    [Size.XL]: "extra large",
    10: "small", // error
    "size": "xs" // error
  }
}
```

`Record` is a generic type util, which allows defining types for key-value maps.

### <a name="Generic_interfaces"></a>4. Generic interfaces

```jsx
interface Item<T> {
  id: number,
  value: T,
}

interface Post {
  title: string;
}

const item1: Item<number> = { id: 1, value: 10 }
const item2: Item<Post> = { id: 1, value: { title: "Post name" } }
```

Generics in typescript significantly improve code reusability. 

### <a name="Generic_function"></a>5. Generic function parameter types

```jsx
interface Book {
  id: number;
  author: string;
}

interface Recipe {
  id: number;
  cookingTime: number;
}

function mapById<T extends { id: number }>(array: T[]): {[key: number]: T} {
  return array.reduce((map, item) => ({ ...map, [item.id]: item }), {})
}

const books: Book[] = [{ id: 1, author: "A" }];
const recipies: Recipe[] = [{ id: 1, cookingTime: 10 }];

mapById(books);
mapById(recipies);
```

It's often the case that we have util functions which require only some field from the interface. In the example above you can see a function which takes an array as input and returns values mapped by id. The only important thing is that the items of the array have an `id`. When you pass a parameter to the function typescript will:
1. Infere the type of the provided parameter
2. Check if the parameter matches specified condition (must have `id` of type number)
So now you are save to use this helper for various interfaces.

### Summary
As you can see typescript provides a lot of tools to write reusable and flexible code without sacrificing type safety. 

Thanks for reading! This post is an experiment with a new format, let me know if you find it interesting and helpful. Help to spread the word and [follow me on Twitter](https://twitter.com/gleb_irovich) for more cool stuff about web dev! 🚀