Skip to content
Theme:

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!
    • a
      asdad

      Been wondering the same

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      Hi. Yes, if you want to keep an interface as close as the initial one, yes, you should keep Omit in place.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • M
    Morteza Sabihi

    Thanks for the great article

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      I am glad you liked it and maybe even learned a thing or two ☺️

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • J
    Javier Cueto

    PartiallyRequired, but intellisense shows the properties, not the confusing utility types:

    type PartiallyRequired<T, K extends keyof T> = { [k in K]-?: T[k] } & { [k in keyof T]: T[k] };
    

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      This is neat. I didn't know about + and - modifiers. Nice solution Javier!

      For anyone interested about how it works, this is a part of official TS documentation about mapping modifiers. Thanks again Javier.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • h
    hipertracker

    Why so hard? You can use:

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

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      Thats the thing. This article is not about creating a new interface, but about creating a helper that can help you with interfaces that you don't have direct access to.

      👆 you can use Markdown here

      Your comment is awaiting moderation. Thanks!
  • C
    Can Rau

    Hey thanks for the nice write-up 🙏

    An alternative I found via SO is

    type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>
    

    Cheers Can

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
  • Y
    Yousaf

    Thats great with types, but is there a way to do this with interfaces?

    👆 you can use Markdown here

    Your comment is awaiting moderation. Thanks!
    • Pawel Grzybek
      Pawel Grzybek

      The example above modifies the interfaces :)

      👆 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!