TIL: Difference between Omit and Exclude in TypeScript

I was trying to use Omit utility type to create a mapped type and it was giving me an error. Please consider the example below.

The Problem

I have a type with union literals like this

type something = 'abc' | 'bcd' | 'cde' | 'def'

I wanted to create a Mapped type like this

type mappedType = {
  [k in something]: string;
}

This is equivalent to

interface mappedType {
  abc: string
  bcd: string
  cde: string
  def: string
}

This works fine but let’s consider if I want to omit one property.

type mappedTypeWithOmit = {
  [k in Omit<something, 'def'>]: string;
}

The above code doesn’t work. Whereas if I use Exclude instead of Omit, I am able to get the desired result:

type mappedTypeWithExclude = {
  [k in Exclude<something, 'def'>]: string;
}

This is strange. I went ahead to check Omit declaration in the typescript repo and saw that it uses Exclude under the hood.

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>

Click to see code in typescript repo

The error I was getting was not helpful either.

Type 'Omit<something, "def">' is not assignable to type 'string | number | symbol'.
  Type 'Omit<something, "def">' is not assignable to type 'string'.

You can also check this in TypeScript Playground

Let’s look at what I was doing wrong.

Omit is used on interface or object type and we are trying to use it on union string literal. That’s why the error.

Usage:

interface mappedType {
  abc: string
  bcd: string
  cde: string
  def: string
}

type mappedTypeWithOmit = Omit<mappedType, 'def'>

This will be equivalent to

interface mappedTypeWithOmit {
  abc: string
  bcd: string
  cde: string
}

Exclude is different, it is used to exclude a union type.

Usage:

type something = 'abc' | 'bcd' | 'cde' | 'def'
type somethingWithExclude = Exlude<something, 'def'>

This will be equivalent to

type somethingWithExclude = 'abc' | 'bcd' | 'cde'

Now we can go ahead and use this to create a mapped type.

type mappedTypeWithExclude = {
  [k in somethingWithExclude]: string;
}

And it will be equivalent to

interface mappedTypeWithExclude {
  abc: string
  bcd: string
  cde: string
}

Tada 🎉

Takeaway

Further Reading

I hope this helps the next time you’re using Omit or Exclude.