TypeScript: Understanding how Omit = Pick + Exclude

christineoo avatar

christineoo

Update: TypeScript 3.5 included the Omit helper type. 🎉

The Pick and Exclude types are part of the utility types provided in TypeScript. The Pick utility type was introduced in TypeScript 2.1. Later on, in TypeScript 2.8, the Exclude type was added. The combination of these two utility types enables the omission type to be written as follows:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

This Omit type was intentionally not included in TypeScript, as mentioned in the TypeScript 2.8 release notes.

We did not include the Omit<T, K> type because it is trivially written as type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

Even though, it may seem trivial but I was slightly overwhelmed by it. Let's decipher the Omit type in this blogpost by breaking it down into 2 parts, Exclude and Pick.

Exclude

First off, Exclude by definition means exclude from T those types that are assignable to U.

type Exclude<T, U> = T extends U ? never : T;
type A = Exclude<'id' | 'name', 'id'>
/**
* type A = 'name'
* 'name' is not assignable to 'id', hence it is excluded
**/

Pick

Next, moving on to Pick. By definition, this means from T, pick a set of properties whose keys are in the union K.

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
type Person = { id: string; name: string }
type C = Pick<Person , 'name'>;
/**
 * keyof Person are 'id' and 'name'
 * Pick<Person , 'name'> = { [P in K]: T[P] }
 *                       = { name: Person['name']}
 *                       = { name: string }
 * Hence, type C = { name: string }
 */

Omit

Finally, going back to Omit. Omit is derived from using Pick and Exclude.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

It is implemented by first finding the types that we want to keep using Exclude , Exclude<keyof T, K>. Then, Pick those types that we want to keep.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Person = { id: string; name: string }

type D = Omit<Person , 'id'>;
/**
 * keyof Person are 'id' and 'name'
 * Exclude<keyof T, K> = Exclude<'id' | 'name', 'id'> = 'name'
 * type D = Omit<Person , 'id'>;
          = Pick<Person, Exclude<keyof Person, 'id'>> 
          = Pick<Person, 'name'> 
          = { name: string }
 * Hence, type D = { name: string }
 */

Deciphering some of the TypeScript types could be overwhelming 😵 . One important thing I've learned is to break down the types into smaller parts and understand them separately before diving head first and trying to understand it as a whole.

💡 Lastly, check out this utility-types library. This library provides an extensive utility types for TypeScript that you may find them useful for your project.

Thanks for reading~! 👋

christineoo avatar
Written By

christineoo

A web developer that is fueled by coffee ☕
Published in JavaScript
Enjoyed the post?

Clap to support the author, help others find it, and make your opinion count.