Derek Lawless

There is always sunshine / Far above the grey sky

The Partial<T> utility type (added in TypeScript 2.1) allows you to take an existing type and make all of its properties optional. This can help in situations where you want to have some flexibility without removing strictness across the board.

Consider the following interface:

interface ChocolateBar {
	readonly manufacturer: string;
	readonly name: string;
	readonly price: Money;
}

As specified, all properties of ChocolateBar are required for the interface contract to be satisfied. (Incidentally, don’t use the Number type for monetary values - introduce a type.)

Inevitably, there will occasions where strictness like this can make life awkward. What if you want to be able to selectively update the values of a chocolate bar? Well, you might write something along the lines of:

interface KeyValuePair {
	readonly key: string;
	readonly value: any;
}

const updateChocolateBar(bar: ChocolateBar, props: KeyValuePair[]): ChocolateBar {
	props.forEach(({ key, value }) => bar[key] = value);
	return bar;
}

While this may be sufficient, each KeyValuePair is ambiguous e.g. { "paws": 4 } is perfectly valid. Ambiguous code is more difficult to reason about, test, and maintain.

A better approach is to use a Partial type:

interface KeyValuePair {
	readonly key: string;
	readonly value: any;
}

const updateChocolateBar(bar: ChocolateBar, props: Partial<ChocolateBar>): ChocolateBar {
	return { ...bar, ...props };
}

Here, Partial<ChocolateBar> creates a type with all properties of ChocolateBar set to optional, effectively producing:

interface ChocolateBar {
	readonly manufacturer?: string;
	readonly name?: string;
	readonly price?: Money;
}

Why is this approach superior?

  1. The ChocolateBar contract is only altered for this specific case
  2. updateChocolateBar() communicates its own contract more effectively, is less ambiguous in use, and straightforward to test
© 2022 Derek Lawless. Built with Gatsby