Skip to content

Software Engineer at Heydoc

Make the TypeScript interface partially optional/required

I came across a situation when I had to make a single key of the TypeScript interface optional. Let’s say that I have a type that consists of two keys, name and age, and I want to make the age key optional. My real-life scenario was more convoluted, but I just want to show you what I learned. Look!

interface Dude {
  name: string;
  age: number;
}

// πŸ‘ OK, name and age are defined
const pawel: Dude = {
  name: "Pawel Grzybek",
  age: 34,
};

// πŸ‘Ž Uuups, age is missing
const dan: Dude = {
  name: "Dan Jordan",
};

TypeScript comes with two handy utility types. The Partial converts all keys to optional and Required that makes all keys mandatory.

interface Dude {
  name: string;
  age: number;
}

type DudeAllOptional = Partial<Dude>;

// πŸ‘ OK, name and age are optional
const dan: DudeAllOptional = {};
interface Dude {
  name: string;
  age?: number;
}

type DudeAllRequired = Required<Dude>;

// πŸ‘Ž Uuups, age is missing
const dan: DudeAllRequired = {
  name: "Dan Jordan",
};

These two utility types are super helpful, but it didn’t solve my problem to make only a subset of keys optional. So I took a moment to brainstorm this idea with my friend Matias (hi dude πŸ‘‹), and we came up with this solution.

interface Dude {
  name: string;
  age: number;
}

type DudeWithOptionalAge = Omit<Dude, "age"> & Partial<Pick<Dude, "age">>;

// πŸ‘ name is defined, age is optional
const dan: DudeWithOptionalAge = {
  name: "Dan Jordan",
};

Problem solved, but we can do better. This type looks ridiculous, and without a good coffee, I don’t want to make any edits to it. But, thanks to generics, we can wrap it in a little utility type and reuse it all over the place.

type PartiallyOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

interface Dude {
  name: string;
  age: number;
}

type DudeWithOptionalAge = PartiallyOptional<Dude, "age">;

// πŸ‘ name is defined, age is optional
const dan: DudeWithOptionalAge = {
  name: "Dan Jordan",
};
type PartiallyRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

interface Dude {
  name: string;
  age?: number;
}

type DudeWithRequiredAge = PartiallyRequired<Dude, "age">;

// πŸ‘Ž Uuups, age is missing
const dan: DudeWithRequiredAge = {
  name: "Dan Jordan",
};

If you know a better solution to my problem then please drop a comment below. If you don’t know a better way of doing it, I hope you learned a thing or two. Until next time, stay curious 🀩

Comments

  • M
    Matias

    hey bro! πŸ‘‹ love pairing with you :D

    If you like this kind of types you'll love fiddling with https://github.com/millsp/ts-toolbelt

    πŸ‘† you can use Markdown here

    Your comment is awaiting moderation. Thanks!
  • B
    Bob Myers

    Is the Omit necessary?

    πŸ‘† you can use Markdown here

    Your comment is awaiting moderation. Thanks!

Leave a comment

πŸ‘† you can use Markdown here

Your comment is awaiting moderation. Thanks!