TypeScript: make selected properties optional with Partial & Omit

When you use the utility Partial on a type, it will make all the type properties optional. Let’s see how we can compose this utility with another one, Omit, to make optional only certain properties of this type. We’ll finally create a utility type using TypeScript generics to apply this on any type we want.

Make all properties of a type optional

The Partial and Omit utilities are well known in TypeScript realm because they’re very handy to quickly apply them to a type and get what is basically a new type. Partial<T> will convert all the keys of a type to be optional. If for example you have this type:

interface Band {
    lead: string
    guitar: string
    bass: string
    drums: string
    keyboard: string
}

all the properties are required when something has this type. If you want to assign an object like this:

const FooFighters: Band = {
    lead: 'Dave Grohl',
    guitar: 'Pat Smears',
    bass: 'Nate Mendel',
    drums: 'Taylor Williams',
}

it will fail because it’s missing the keyboard property. Now, we could mark this property as optional and TypeScript easily allows us to do so. The most basic way to do it is by appending a question mark to its name. In this case, if it’s possible to edit the original type, you can go to it and modify it like this:

interface Band {
    lead: string
    guitar: string
    bass: string
    drums: string
    keyboard?: string
}

Great. Now the previous assignment will succeed because keyboard is no longer required. But what if it’s not possible to change the original type for whatever reason? What you can quickly do in these cases is to apply Partial to this type:

type NotAllTheBand = Partial<Band>

const FooFighters: NotAllTheBand = {
    lead: 'Dave Grohl',
    guitar: 'Pat Smears',
    bass: 'Nate Mendel',
    drums: 'Taylor Williams',
}

This stops enforcing all properties, making all of them optional. Maybe sometimes we want this, but what if we just want to make only one property optional, and avoid modifying the original type?

Removing properties from a type

Before we move on to it and see how it works, let’s see another utility that will help us with this: Omit. This utility type removes properties from the type where it’s applied. While Partial doesn’t take other arguments than the type, Omit takes the type plus the properties you want to remove: Omit<T, K>.

It’s not like Partial that makes them optional: Omit will completely obliterate them from the type. When you use Partial<Band> it’s as if you’ve done this:

interface Band {
    lead?: string
    guitar?: string
    bass?: string
    drums?: string
    keyboard?: string
}

However, when you use Omit<Band, 'keyboard'>, it’s as if you’ve done this:

interface Band {
    lead: string
    guitar: string
    bass: string
    drums: string
}

This is now starting to make sense, right? Can you imagine what we could do in TypeScript if we have a type that has all the properties of the original set to optional and another type that has only the properties that we want to require? If only there was a way of… intersecting these types, right?

Intersecting types

Yes, there’s a way in TypeScript using the ampersand &, the type intersection operator. This operator, given two types, constructs a new one with the properties that belong to both types:

interface SomeType {
    propA: string
    propB: number
}

interface AnotherType {
    propC: boolean
    propD: Array<string>
}

// New type includes propA, propB, propC, and propD
type IntersectedType = SomeType & AnotherType

By now you probably figured it out: we’re going to intersect a partialized type and a type where we removed some of its properties.

Make selected properties of a type optional

Let’s try to make sense of it. It’s important when learning TypeScript to rationalize what’s happening with our types.

We’re going to grab a type that has all its properties set to optional thanks to Partial applied on it. Next, we’re going to intersect it with a type that has selected properties removed by Omit. Let’s see an example:

interface SomeType {
    propA: string
    propB: number
}

type OptionalPropB = Partial<SomeType> & Omit<SomeType, 'propB'>

We now have a new type, without modifying the original type, that looks like the following:

type OptionalPropB = {
    propA: string
    propB?: string
}

This happens because we’re grabbing properties propA? and propB? from the type produced by Partial and intersecting them with propA from the type produced by Omit.

TypeScript generic utilities

Ok, now our new type works and we can have only selected properties marked as optional without modifying the original type. However, this line

type OptionalPropB = Partial<SomeType> & Omit<SomeType, 'propB'>

Is too verbose, quite ugly and tedious to write every time. More importantly, it won’t work for a different type, we’ll have to write it again every time. Can we make it shorter & prettier, and make it work for any type? Yes, TypeScript allows us to use generics here and create our own utility type:

type Optional<T, K extends keyof T> = Partial<T> & Omit<T, K>

And now we have our own TypeScript utility type implemented with generics that we can use to make selected type properties optional in any type. We can use it like this:

type TypeWithOptionalProp = Optional<SomeType, 'propB'>

You’ll notice the signature is similar to Omit: this utility takes the type to operate on, and the properties that will be made optional. If you have more, you can use the union operator:

type TypeWithOptionalProps = Optional<SomeType, 'propB' | 'propC'>

Closing words

You can now save this generic utility type in a file and export it to use it throughout your entire app. Here’s a link to a TypeScript playground where I added the code from this article and commented it so you can see what’s going on, looking at Partial, Omit, and our generic utility type.

The world of generics and utilities is fantastic in TypeScript. Get into it and start experimenting!

Leave a Reply