Optional properties with utility types
Use TypeScript's utility types to set object properties to optional
O ne of TypeScript’s powerful features is its ability to create optional types. Optional types are a way of telling TypeScript that a property does not have to be provided.
Let’s learn how to combine some utility types to help us create a helper type that allows us to specify which properties should be optional.
Creating an optional property
The syntax for creating an optional property is to add a question mark (?) right after the property name.
type Car = {
brand: string;
color: string;
engine?: string;
};You might be thinking that we could set the engine property type to string or undefined but this
is not the case.
TypeScript requires us to explicitly assign a value for engine - even if it is undefined.
type Car = {
brand: string;
color: string;
engine: string | undefined;
};
const car: Car = {
brand: "Ford",
color: "blue",
};
// ❌ Property 'engine' is missing in type '{ brand: string; color: string; }' but required in type 'Car'
const validCar: Car = {
brand: "Ford",
color: "blue",
engine: undefined,
};Partial
We have access to a utility type provided by TypeScript named Partial. Partial sets
all of an object types’s properties to optional.
type Car = {
brand: string;
color: string;
engine: string;
};
type CarPartial = Partial<Car>;
const validCar: CarPartial = {
brand: "Ford",
};Pick
TypeScript also provides a utility type called Pick that allows us to create a new type
by picking the properties we want from an existing type. A union of properties can be provided to
pick multiple properties.
type Car = {
brand: string;
color: string;
engine: string;
};
type BrandAndColor = Pick<Car, "brand" | "color">;
// From type Car, create me a new type containing only 'brand' and 'color'.
const pickedCar: BrandAndColor = {
brand: "Ford",
color: "blue",
};Omit
Omit is another utility type that allows us to create a new type by excluding the properties we want from an existing type.
Exactly like Pick, we can provide a union to omit multiple properties.
type Car = {
brand: string;
color: string;
engine: string;
};
type EngineOnly = Omit<Car, "brand" | "color">;
// From type Car, create me a new type omitting 'brand' and 'color'.
const engineOnlyCar: EngineOnly = {
engine: "V8",
};Utility types, Assemble!
Let’s stick with our Car type and imagine we want to make a new type that
sets only engine to be optional. With our knowledge of how the three utilites, Partial, Pick, and Omit work,
we can create our new type.
Think of this as three steps:
Create a new object type, picking the properties we want to make optional in our end result, using
Pick.Use
Partialto set all of the properties in the new object type to optional.Use
Omitto create a new object type excluding the properties we want to make optional, using an intersection type to join it all together.
type Car = {
brand: string;
color: string;
engine: string;
};
type MakePropertyOptional<Obj, Prop extends keyof Obj> = Partial<
Pick<Obj, Prop>
> &
Omit<Obj, Prop>;
type OptionalEngine = MakePropertyOptional<Car, "engine">;
const car: OptionalEngine = {
brand: "Ford",
color: "blue",
};Improvements
If you hover over the OptionalEngine type in your IDE, it isn’t very pretty or intuitive.
For an improved developer experience, we can create a Prettify helper type to make it easier to decipher.
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
type Result = Prettify<OptionalEngine>;
/** ✨
* {
* brand: string;
* color: string;
* engine?: string | undefined;
* }
*/ ✨It works with unions too!
The great thing about Partial, Omit and Pick is that they work
with union types which means our MakePropertyOptional helper
works with them too!
type Car = {
brand: string;
color: string;
engine: string;
};
type MakePropertyOptional<Obj, Prop extends keyof Obj> = Partial<
Pick<Obj, Prop>
> &
Omit<Obj, Prop>;
type OptionalEngineAndColor = MakePropertyOptional<Car, "engine" | "color">
type Result = Prettify<OptionalEngineAndColor>;
/** ✨
* {
* brand: string;
* color?: string | undefined;
* engine?: string | undefined;
* }
*/ ✨