5 Typescript utility types, that will make your life easier
Tags: typescript, tutorial, beginners, javascript
Published: 2020-07-06 19:26:27

Luckily for us, developers, Typescript is shipped with a lot of handy utility types. They are meant to improve the readability of the code and reduce the boilerplate while working with types. In today's episode of Typescript 101, I would like to talk about 5 utility types, which I find especially useful in everyday development.

### Table of content

- [Omit](#omit)
- [Pick](#pick)
- [Partial](#partial)
- [Readonly](#readonly)
- [Record](#record)

### <a name="omit"></a>1. Omit

`Omit<T, K>` according to the official documentation `Constructs a type by picking all properties from T and then removing K.` In other words `Omit` is a generic utility type, that drops keys of `T` specified in `K`. One of the use-cases where you might need this utility is working with DTOs. If your project is using a strict serialisation, you might find yourself creating a lot of boilerplate code to describe different DTOs. Let's consider an example of how we can benefit from `Omit` in that case:

```tsx
interface Post {
  id: number;
  title: string;
  createdAt: string;
}

type CreatePostDto = Omit<Post, "id" | "createdAt">;

const createDto: CreatePostDto = {
  title: "My post",
  id: 1, // error
  createdAt: "2020-06-06" // error
};
```

Properties like `id` or `createdAt` are usually set by the backend and you don't have them available while creating a new entity via the API. You can simply describe this situation by omitting those keys from the `Post` interface.

### <a name="pick"></a>2. Pick

`Pick` does the opposite of `Omit`. `Pick<T, K>` `Constructs a type by picking the set of properties K from T.` Continuing the same example with DTOs, here is how you can define a type of `UpdatePostDto` :

```tsx
type UpdatePostDto = Pick<Post, "id" | "title">;

const updateDto: UpdatePostDto = {
  id: 1,
  title: "My new post",
  createdAt: "2020-06-06" // error
};
```

`Pick` and `Omit` can be used to achieve the same goal because `Pick<Post, "id" | "title">` is the same as `Omit<Post, "createdAt">`. You can always decide what is shorter or more readable to use.

### <a name="partial"></a>3. Partial

`Partial<T>` is a generic utility type, that makes all properties of the provided interface optional. My favourite example of using `Partial` is updating objects via merging. It's especially common when you are working with state management and state updates.

```tsx
interface AppState {
  posts: Post[];
  userName: string;
}

function updateState(state: AppState, update: Partial<AppState>): AppState {
  return { ...state, ...update };
}

const initialState: AppState = {
  posts: [],
  userName: "Gleb"
};

const update: Partial<AppState> = {
  userName: "John"
};

updateState(initialState, update);
```

`Partial` sets all properties of `AppState` to optional and therefore allows you to define only updated keys, without losing type safety.

### <a name="readonly"></a>4. Readonly

`Readonly<T>` is another handy utility, which helps while working with immutable data. If you'd like to enforce immutability try using `Readonly`:

```tsx
const state: Readonly<AppState> = {
  posts: [],
  userName: "Gleb"
};

state.posts = []; // error: Cannot assign to 'posts' because it is a read-only property.
const updatedState: Readonly<AppState> = { ...state, posts: [] }; // ok
```

### <a name="record"></a>5. Record

I have already talked about `Record<T, K>` in [this post](https://dev.to/glebirovich/5-typescript-features-that-will-improve-your-codebase-hok#Enum_object_keys), but this utility is definitely worth mentioning one more time.
During my daily duties, I have to deal a lot with data grids. Most of them have a very similar pattern: they define every row as a key-value map. It's often the case that the row interface can be defined quite loosely:

```tsx
type Cell = string;

interface Row {
  [key: string]: Cell;
}
```

It means that you can potentially add as many keys as you want. Here is an example of the row that represents a single post object:

```tsx
const post: Post = { id: 1, title: "My post", createdAt: "2020-06-06" };

const row: Row = {
  title: post.title,
  createdAt: post.createdAt,
  randomKey: "is allowed"
};
```

Luckily there is a nice way to constrain allowed keys by using `Record`:

```tsx
type PostRow = Record<keyof Post, Cell>;

const postRow: PostRow = {
  id: post.id.toString(),
  title: post.title,
  createdAt: post.createdAt,
  randomKey: "is not allowed" // error
};
```

This approach makes the expected type of the row transparent to the developers and keeps it type-safe. Also with just a little effort, you can create a reusable generic row type:

```tsx
type PostRow<T> = Record<keyof T, Cell>;

const postRow: PostRow<Post> = {
  id: post.id.toString(),
  title: post.title,
  createdAt: post.createdAt,
  randomKey: "is not allowed" // error
}; 
```

### Summary

Today we discovered some superpowers of Typescript utility types and I hope you have enjoyed exploring them with me! I am very interested in your feedback. If you want to learn something specific about Typescript or webdev in general, leave a comment and let's discuss it together.

If you liked my post, please spread a word and [follow me on Twitter](https://twitter.com/gleb_irovich) 🚀 for more exciting content about web development.