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 🤩
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
Is the
Omit
necessary?Been wondering the same
Hi. Yes, if you want to keep an interface as close as the initial one, yes, you should keep
Omit
in place.Thanks for the great article
I am glad you liked it and maybe even learned a thing or two ☺️
PartiallyRequired, but intellisense shows the properties, not the confusing utility types:
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.
Why so hard? You can use:
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.
Hey thanks for the nice write-up 🙏
An alternative I found via SO is
Cheers Can
Thats great with types, but is there a way to do this with interfaces?
The example above modifies the interfaces :)