DEV Community

derek lawless
derek lawless

Posted on • Updated on • Originally published at dereklawless.ie

Partial Types in TypeScript

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;
}
~~~{% endraw %}

As specified, all properties of {% raw %}`ChocolateBar`{% endraw %} are required for the interface contract to be satisfied. (Incidentally, don't use the {% raw %}`Number` type for monetary values - [introduce a type](https://martinfowler.com/eaaCatalog/money.html).)

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:

~~~typescript
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:

~~~typescript
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:

~~~typescript
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_
1. `updateChocolateBar()` communicates its own contract more effectively, is less ambiguous in use, and straightforward to test
Enter fullscreen mode Exit fullscreen mode

Top comments (0)